diff --git a/packages/-ember-data/tests/deprecations/deprecate-early-static-test.js b/packages/-ember-data/tests/deprecations/deprecate-early-static-test.js new file mode 100644 index 00000000000..2fc8135dd64 --- /dev/null +++ b/packages/-ember-data/tests/deprecations/deprecate-early-static-test.js @@ -0,0 +1,63 @@ +import { module } from 'qunit'; + +import { setupTest } from 'ember-qunit'; + +import Model from '@ember-data/model'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; + +module('Deprecations', function (hooks) { + setupTest(hooks); + + const StaticModelMethods = [ + { name: 'typeForRelationship', count: 3 }, + { name: 'inverseFor', count: 6 }, + { name: '_findInverseFor', count: 4 }, + { name: 'eachRelationship', count: 3 }, + { name: 'eachRelatedType', count: 3 }, + { name: 'determineRelationshipType', count: 1 }, + { name: 'eachAttribute', count: 2 }, + { name: 'eachTransformedAttribute', count: 4 }, + { name: 'toString', count: 1 }, + ]; + const StaticModelGetters = [ + { name: 'inverseMap', count: 1 }, + { name: 'relationships', count: 3 }, + { name: 'relationshipNames', count: 1 }, + { name: 'relatedTypes', count: 2 }, + { name: 'relationshipsByName', count: 2 }, + { name: 'relationshipsObject', count: 1 }, + { name: 'fields', count: 1 }, + { name: 'attributes', count: 1 }, + { name: 'transformedAttributes', count: 3 }, + ]; + + function checkDeprecationForProp(prop) { + deprecatedTest( + `Accessing static prop ${prop.name} is deprecated`, + { id: 'ember-data:deprecate-early-static', until: '5.0', count: prop.count }, + function (assert) { + class Post extends Model {} + Post[prop.name]; + assert.ok(true); + } + ); + } + function checkDeprecationForMethod(method) { + deprecatedTest( + `Accessing static method ${method.name} is deprecated`, + { id: 'ember-data:deprecate-early-static', until: '5.0', count: method.count }, + function (assert) { + class Post extends Model {} + try { + Post[method.name](); + } catch { + // do nothing + } + assert.ok(true); + } + ); + } + + StaticModelGetters.forEach(checkDeprecationForProp); + StaticModelMethods.forEach(checkDeprecationForMethod); +}); diff --git a/packages/-ember-data/tests/deprecations/deprecate-reopen-class-test.js b/packages/-ember-data/tests/deprecations/deprecate-reopen-class-test.js new file mode 100644 index 00000000000..9e4e5dd1f2f --- /dev/null +++ b/packages/-ember-data/tests/deprecations/deprecate-reopen-class-test.js @@ -0,0 +1,30 @@ +import { module } from 'qunit'; + +import { setupTest } from 'ember-qunit'; + +import Model from '@ember-data/model'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; + +module('Deprecations', function (hooks) { + setupTest(hooks); + + deprecatedTest( + `Calling on natively extended class`, + { id: 'ember-data:deprecate-model-reopenclass', until: '5.0', count: 1 }, + function (assert) { + class Post extends Model {} + Post.reopenClass({}); + assert.ok(true); + } + ); + + deprecatedTest( + `Calling on classic extended class`, + { id: 'ember-data:deprecate-model-reopenclass', until: '5.0', count: 1 }, + function (assert) { + const Post = Model.extend(); + Post.reopenClass({}); + assert.ok(true); + } + ); +}); diff --git a/packages/-ember-data/tests/deprecations/deprecate-reopen-test.js b/packages/-ember-data/tests/deprecations/deprecate-reopen-test.js new file mode 100644 index 00000000000..4cf4ac2c847 --- /dev/null +++ b/packages/-ember-data/tests/deprecations/deprecate-reopen-test.js @@ -0,0 +1,30 @@ +import { module } from 'qunit'; + +import { setupTest } from 'ember-qunit'; + +import Model from '@ember-data/model'; +import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; + +module('Deprecations', function (hooks) { + setupTest(hooks); + + deprecatedTest( + `Calling on natively extended class`, + { id: 'ember-data:deprecate-model-reopen', until: '5.0', count: 1 }, + function (assert) { + class Post extends Model {} + Post.reopen({}); + assert.ok(true); + } + ); + + deprecatedTest( + `Calling on classic extended class`, + { id: 'ember-data:deprecate-model-reopen', until: '5.0', count: 1 }, + function (assert) { + const Post = Model.extend(); + Post.reopen({}); + assert.ok(true); + } + ); +}); diff --git a/packages/-ember-data/tests/integration/debug-adapter-test.js b/packages/-ember-data/tests/integration/debug-adapter-test.js index 01a22f2d5be..e46155a6fb1 100644 --- a/packages/-ember-data/tests/integration/debug-adapter-test.js +++ b/packages/-ember-data/tests/integration/debug-adapter-test.js @@ -15,24 +15,22 @@ import Model, { attr } from '@ember-data/model'; if (has('@ember-data/debug')) { const DebugAdapter = require('@ember-data/debug').default; - class Post extends Model { - @attr() - title; - } - - module('integration/debug-adapter - DS.DebugAdapter', function (hooks) { + module('integration/debug-adapter - DebugAdapter', function (hooks) { setupTest(hooks); let store; hooks.beforeEach(function () { let { owner } = this; + class Post extends Model { + @attr title; + } owner.register('model:post', Post); store = owner.lookup('service:store'); let _adapter = DebugAdapter.extend({ getModelTypes() { - return A([{ klass: Post, name: 'post' }]); + return A([{ klass: store.modelFor('post'), name: 'post' }]); }, }); owner.register('data-adapter:main', _adapter); @@ -205,14 +203,13 @@ if (has('@ember-data/debug')) { let { owner } = this; let debugAdapter = owner.lookup('data-adapter:main'); class Person extends Model { - @attr() - title; - - @attr() - firstOrLastName; + @attr title; + @attr firstOrLastName; } + owner.register('model:person', Person); + const store = owner.lookup('service:store'); - const columns = debugAdapter.columnsForType(Person); + const columns = debugAdapter.columnsForType(store.modelFor('person')); assert.strictEqual(columns[0].desc, 'Id'); assert.strictEqual(columns[1].desc, 'Title'); diff --git a/packages/-ember-data/tests/integration/records/error-test.js b/packages/-ember-data/tests/integration/records/error-test.js index ff41fcf8c4b..a945ef6bd71 100644 --- a/packages/-ember-data/tests/integration/records/error-test.js +++ b/packages/-ember-data/tests/integration/records/error-test.js @@ -197,22 +197,20 @@ module('integration/records/error', function (hooks) { let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - adapter.reopen({ - createRecord() { - return RSVP.reject( - new InvalidError([ - { - detail: 'Must be unique', - source: { pointer: '/data/attributes/first-name' }, - }, - { - detail: 'Must not be blank', - source: { pointer: '/data/attributes/last-name' }, - }, - ]) - ); - }, - }); + adapter.createRecord = () => { + return RSVP.reject( + new InvalidError([ + { + detail: 'Must be unique', + source: { pointer: '/data/attributes/first-name' }, + }, + { + detail: 'Must not be blank', + source: { pointer: '/data/attributes/last-name' }, + }, + ]) + ); + }; let person = store.createRecord('person'); diff --git a/packages/-ember-data/tests/integration/records/unload-test.js b/packages/-ember-data/tests/integration/records/unload-test.js index 26a2e7bd225..4c87f0c98d3 100644 --- a/packages/-ember-data/tests/integration/records/unload-test.js +++ b/packages/-ember-data/tests/integration/records/unload-test.js @@ -16,76 +16,71 @@ function idsFromArr(arr) { return arr.map((i) => i.id); } -const Person = Model.extend({ - name: attr('string'), +class Person extends Model { + @attr('string') name; // 1:many sync - cars: hasMany('car', { async: false }), + @hasMany('car', { async: false }) cars; // 1:many async - boats: hasMany('boat', { async: true }), + @hasMany('boat', { async: true }) boats; // many:many sync - groups: hasMany('group', { async: false }), + @hasMany('group', { async: false }) groups; // many:many async - friends: hasMany('people', { async: true }), + @hasMany('people', { async: true }) friends; // 1:1 sync inverse null - bike: belongsTo('bike', { async: false, inverse: null }), + @belongsTo('bike', { async: false, inverse: null }) bike; // 1:1 sync - house: belongsTo('house', { async: false }), + @belongsTo('house', { async: false }) house; // 1:1 async - mortgage: belongsTo('mortgage', { async: true }), + @belongsTo('mortgage', { async: true }) mortgage; // 1 async : 1 sync - favoriteBook: belongsTo('book', { async: false }), + @belongsTo('book', { async: false }) favoriteBook; // 1 async : many sync - favoriteSpoons: hasMany('spoon', { async: false }), + @hasMany('spoon', { async: false }) favoriteSpoons; // 1 sync: many async - favoriteShows: hasMany('show', { async: true }), + @hasMany('show', { async: true }) favoriteShows; // many sync : many async - favoriteFriends: hasMany('people', { async: true, inverse: 'favoriteAsyncFriends' }), + @hasMany('people', { async: true, inverse: 'favoriteAsyncFriends' }) favoriteFriends; // many async : many sync - favoriteAsyncFriends: hasMany('people', { async: false, inverse: 'favoriteFriends' }), -}); -Person.reopenClass({ - toString() { + @hasMany('people', { async: false, inverse: 'favoriteFriends' }) favoriteAsyncFriends; + + static toString() { return 'Person'; - }, -}); + } +} -const House = Model.extend({ - person: belongsTo('person', { async: false }), -}); -House.reopenClass({ - toString() { +class House extends Model { + @belongsTo('person', { async: false }) person; + + static toString() { return 'House'; - }, -}); + } +} -const Mortgage = Model.extend({ - person: belongsTo('person', { async: true }), -}); -Mortgage.reopenClass({ - toString() { +class Mortgage extends Model { + @belongsTo('person', { async: true }) person; + + static toString() { return 'Mortgage'; - }, -}); + } +} -const Group = Model.extend({ - people: hasMany('person', { async: false }), -}); -Group.reopenClass({ - toString() { +class Group extends Model { + @hasMany('person', { async: false }) people; + + static toString() { return 'Group'; - }, -}); + } +} -const Car = Model.extend({ - make: attr('string'), - model: attr('string'), - person: belongsTo('person', { async: false }), -}); -Car.reopenClass({ - toString() { +class Car extends Model { + @attr('string') make; + @attr('string') model; + @belongsTo('person', { async: false }) person; + + static toString() { return 'Car'; - }, -}); + } +} const Boat = Model.extend({ name: attr('string'), diff --git a/packages/-ember-data/tests/integration/relationships/belongs-to-test.js b/packages/-ember-data/tests/integration/relationships/belongs-to-test.js index 43f9354d33e..a7033416a88 100644 --- a/packages/-ember-data/tests/integration/relationships/belongs-to-test.js +++ b/packages/-ember-data/tests/integration/relationships/belongs-to-test.js @@ -13,8 +13,6 @@ import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in import { getRelationshipStateForRecord, hasRelationshipForRecord } from '../../helpers/accessors'; -let Book, Chapter; - module('integration/relationship/belongs-to BelongsTo Relationships (new-style)', function (hooks) { let store; setupTest(hooks); @@ -271,45 +269,44 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function setupTest(hooks); hooks.beforeEach(function () { - const User = Model.extend({ - name: attr('string'), - messages: hasMany('message', { polymorphic: true, async: false }), - favouriteMessage: belongsTo('message', { polymorphic: true, inverse: null, async: false }), - }); - - const Message = Model.extend({ - user: belongsTo('user', { inverse: 'messages', async: false }), - created_at: attr('date'), - }); + class User extends Model { + @attr('string') name; + @hasMany('message', { polymorphic: true, async: false, inverse: 'user' }) messages; + @belongsTo('message', { polymorphic: true, inverse: null, async: false }) favouriteMessage; + } - const Post = Message.extend({ - title: attr('string'), - comments: hasMany('comment', { async: false, inverse: null }), - }); + class Message extends Model { + @belongsTo('user', { inverse: 'messages', async: false }) user; + @attr('date') created_at; + } - const Comment = Message.extend({ - body: attr('string'), - message: belongsTo('message', { polymorphic: true, async: false, inverse: null }), - }); + class Comment extends Message { + @attr('string') body; + @belongsTo('message', { polymorphic: true, async: false, inverse: null }) message; + } + class Post extends Message { + @attr('string') title; + @hasMany('comment', { async: false, inverse: null }) comments; + } - Book = Model.extend({ - name: attr('string'), - author: belongsTo('author', { async: false }), - chapters: hasMany('chapters', { async: false, inverse: 'book' }), - }); + class Book extends Model { + @attr name; + @belongsTo('author', { async: false, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } const Book1 = Model.extend({ name: attr('string'), }); - Chapter = Model.extend({ - title: attr('string'), - book: belongsTo('book', { async: false, inverse: 'chapters' }), - }); + class Chapter extends Model { + @attr title; + @belongsTo('book', { async: false, inverse: 'chapters' }) book; + } const Author = Model.extend({ name: attr('string'), - books: hasMany('books', { async: false }), + books: hasMany('books', { async: false, inverse: 'author' }), }); const Section = Model.extend({ @@ -337,10 +334,6 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }, }) ); - - const store = this.owner.lookup('service:store'); - Book = store.modelFor('book'); - Chapter = store.modelFor('chapter'); }); test('returning a null relationship from payload sets the relationship to null on both sides', function (assert) { @@ -428,17 +421,21 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('The store can materialize a non loaded monomorphic belongsTo association', function (assert) { assert.expect(1); + class Message extends Model { + @belongsTo('user', { inverse: 'messages', async: false }) user; + @attr('date') created_at; + } + class Post extends Message { + @attr('string') title; + @hasMany('comment', { async: false, inverse: null }) comments; + @belongsTo('user', { async: true, inverse: 'messages' }) user; + } + this.owner.register('model:message', Message); + this.owner.register('model:post', Post); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - user: belongsTo('user', { - async: true, - inverse: 'messages', - }), - }); - adapter.shouldBackgroundReloadRecord = () => false; adapter.findRecord = function (store, type, id, snapshot) { assert.ok(true, "The adapter's find method should be called"); @@ -960,16 +957,23 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { - async: true, - inverse: 'post', - }), - }); + class Message extends Model { + @attr('date') created_at; + } + class Post extends Message { + @attr('string') title; + @hasMany('comment', { async: true, inverse: 'post' }) comments; + } - store.modelFor('comment').reopen({ - post: belongsTo('post', { async: false }), - }); + class Comment extends Message { + @attr('string') body; + @belongsTo('message', { polymorphic: true, async: false, inverse: null }) message; + @belongsTo('post', { async: false, inverse: 'comments' }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:message', Message); + this.owner.register('model:comment', Comment); let comment; run(() => { @@ -1039,18 +1043,25 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function let adapter = store.adapterFor('application'); let post; - store.modelFor('message').reopen({ - user: hasMany('user', { - async: true, - }), - }); + class Message extends Model { + @attr('date') created_at; + @hasMany('user', { async: true, inverse: 'messages' }) user; + } + class Post extends Message { + @attr('string') title; + @hasMany('comment', { async: true, inverse: 'post' }) comments; + @belongsTo('user', { async: true, inverse: 'messages' }) user; + } - store.modelFor('post').reopen({ - user: belongsTo('user', { - async: true, - inverse: 'messages', - }), - }); + class Comment extends Message { + @attr('string') body; + @belongsTo('message', { polymorphic: true, async: false, inverse: null }) message; + @belongsTo('post', { async: false, inverse: 'user' }) messages; + } + + this.owner.register('model:post', Post); + this.owner.register('model:message', Message); + this.owner.register('model:comment', Comment); run(() => { post = store.push({ @@ -1126,12 +1137,14 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); test('Rollbacking attributes for a deleted record restores implicit relationship - async', function (assert) { - Book.reopen({ - author: belongsTo('author', { async: true }), - }); + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); - let book, author; run(() => { book = store.push({ @@ -1214,7 +1227,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); testInDebug('Passing a model as type to belongsTo should not work', function (assert) { - assert.expect(1); + assert.expect(2); assert.expectAssertion(() => { const User = Model.extend(); @@ -1223,15 +1236,17 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function user: belongsTo(User, { async: false }), }); }, /The first argument to belongsTo must be a string/); + assert.expectDeprecation({ id: 'ember-data:deprecate-early-static' }); }); test('belongsTo hasAnyRelationshipData async loaded', function (assert) { assert.expect(1); - - Book.reopen({ - author: belongsTo('author', { async: true }), - }); - + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1285,11 +1300,12 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('belongsTo hasAnyRelationshipData async not loaded', function (assert) { assert.expect(1); - - Book.reopen({ - author: belongsTo('author', { async: true }), - }); - + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1340,11 +1356,12 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('belongsTo hasAnyRelationshipData NOT created', function (assert) { assert.expect(2); - - Book.reopen({ - author: belongsTo('author', { async: true }), - }); - + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); run(() => { @@ -1453,10 +1470,12 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('Related link should be fetched when no relationship data is present', function (assert) { assert.expect(3); - - Book.reopen({ - author: belongsTo('author', { async: true }), - }); + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1497,9 +1516,12 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('Related link should take precedence over relationship data if no local record data is available', async function (assert) { assert.expect(2); - Book.reopen({ - author: belongsTo('author', { async: true }), - }); + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1542,9 +1564,12 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('Relationship data should take precedence over related link when local record data is available', function (assert) { assert.expect(1); - Book.reopen({ - author: belongsTo('author', { async: true }), - }); + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1592,9 +1617,12 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('New related link should take precedence over local data', function (assert) { assert.expect(3); - Book.reopen({ - author: belongsTo('author', { async: true }), - }); + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1654,9 +1682,12 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('Updated related link should take precedence over relationship data and local record data', function (assert) { assert.expect(4); - Book.reopen({ - author: belongsTo('author', { async: true }), - }); + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1734,9 +1765,12 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function test('Updated identical related link should not take precedence over local data', function (assert) { assert.expect(2); - Book.reopen({ - author: belongsTo('author', { async: true }), - }); + class Book extends Model { + @attr name; + @belongsTo('author', { async: true, inverse: 'books' }) author; + @hasMany('chapter', { async: false, inverse: 'book' }) chapters; + } + this.owner.register('model:book', Book); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1802,9 +1836,11 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); test('A belongsTo relationship can be reloaded using the reference if it was fetched via link', function (assert) { - Chapter.reopen({ - book: belongsTo({ async: true }), - }); + class Chapter extends Model { + @attr title; + @belongsTo('book', { async: true, inverse: 'chapters' }) book; + } + this.owner.register('model:chapter', Chapter); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1865,10 +1901,6 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); test('A synchronous belongsTo relationship can be reloaded using a reference if it was fetched via id', function (assert) { - Chapter.reopen({ - book: belongsTo({ async: false }), - }); - let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1920,9 +1952,11 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); test('A belongsTo relationship can be reloaded using a reference if it was fetched via id', function (assert) { - Chapter.reopen({ - book: belongsTo({ async: true }), - }); + class Chapter extends Model { + @attr title; + @belongsTo('book', { async: true, inverse: 'chapters' }) book; + } + this.owner.register('model:chapter', Chapter); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -1998,9 +2032,11 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function }); test("belongsTo relationship with links doesn't trigger extra change notifications - #4942", function (assert) { - Chapter.reopen({ - book: belongsTo({ async: true }), - }); + class Chapter extends Model { + @attr title; + @belongsTo('book', { async: true, inverse: 'chapters' }) book; + } + this.owner.register('model:chapter', Chapter); let store = this.owner.lookup('service:store'); diff --git a/packages/-ember-data/tests/integration/relationships/has-many-test.js b/packages/-ember-data/tests/integration/relationships/has-many-test.js index b9a898456ff..84c1379212e 100644 --- a/packages/-ember-data/tests/integration/relationships/has-many-test.js +++ b/packages/-ember-data/tests/integration/relationships/has-many-test.js @@ -23,44 +23,38 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( setupTest(hooks); hooks.beforeEach(function () { - const User = Model.extend({ - name: attr('string'), - messages: hasMany('message', { polymorphic: true, async: false }), - contacts: hasMany('user', { inverse: null, async: false }), - }); + class User extends Model { + @attr name; + @hasMany('message', { polymorphic: true, async: false, inverse: 'user' }) messages; + @hasMany('user', { inverse: null, async: false }) contacts; + } - const Contact = Model.extend({ - user: belongsTo('user', { async: false }), - toString: () => 'Contact', - }); + class Contact extends Model { + @belongsTo('user', { async: false, inverse: null }) user; + } - const Email = Contact.extend({ - email: attr('string'), - toString: () => 'Email', - }); + class Email extends Contact { + @attr email; + } - const Phone = Contact.extend({ - number: attr('string'), - toString: () => 'Phone', - }); + class Phone extends Contact { + @attr number; + } - const Message = Model.extend({ - user: belongsTo('user', { async: false }), - created_at: attr('date'), - toString: () => 'Message', - }); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false, inverse: 'messages' }) user; + } - const Post = Message.extend({ - title: attr('string'), - comments: hasMany('comment', { async: false }), - toString: () => 'Post', - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: false, inverse: 'message' }) comments; + } - const Comment = Message.extend({ - body: attr('string'), - message: belongsTo('post', { polymorphic: true, async: true }), - toString: () => 'Comment', - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { polymorphic: true, async: true, inverse: 'comments' }) message; + } const Book = Model.extend({ title: attr(), @@ -68,17 +62,15 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( toString: () => 'Book', }); - const Chapter = Model.extend({ - title: attr(), - pages: hasMany('page', { async: false }), - toString: () => 'Chapter', - }); + class Chapter extends Model { + @attr title; + @hasMany('page', { async: false, inverse: 'chapter' }) pages; + } - const Page = Model.extend({ - number: attr('number'), - chapter: belongsTo('chapter', { async: false }), - toString: () => 'Page', - }); + class Page extends Model { + @attr number; + @belongsTo('chapter', { async: false, inverse: 'pages' }) chapter; + } this.owner.register('model:user', User); this.owner.register('model:contact', Contact); @@ -490,14 +482,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( // common use case of this is to provide a URL to a collection that // is loaded later. test("A serializer can materialize a hasMany as an opaque token that can be lazily fetched via the adapter's findHasMany hook", function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - const Post = store.modelFor('post'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - Post.reopen({ - comments: hasMany('comment', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { polymorphic: true, async: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); // When the store asks the adapter for the record with ID 1, // provide some fake data. @@ -553,17 +557,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Accessing a hasMany backed by a link multiple times triggers only one request', function (assert) { assert.expect(2); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); - store.modelFor('comment').reopen({ - message: belongsTo('post', { async: true }), - }); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); let post; @@ -627,17 +640,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('A hasMany backed by a link remains a promise after a record has been added to it', function (assert) { assert.expect(1); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); - store.modelFor('comment').reopen({ - message: belongsTo('post', { async: true }), - }); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.findHasMany = function (store, snapshot, link, relationship) { return resolve({ @@ -688,16 +710,25 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('A hasMany updated link should not remove new children', function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - store.modelFor('comment').reopen({ - message: belongsTo('post', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.findHasMany = function (store, snapshot, link, relationship) { return resolve({ data: [] }); @@ -736,16 +767,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('A hasMany updated link should not remove new children when the parent record has children already', function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - store.modelFor('comment').reopen({ - message: belongsTo('post', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.findHasMany = function (store, snapshot, link, relationship) { return resolve({ @@ -785,16 +826,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test("A hasMany relationship doesn't contain duplicate children, after the canonical state of the relationship is updated via store#push", function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - store.modelFor('comment').reopen({ - message: belongsTo('post', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.createRecord = function (store, snapshot, link, relationship) { return resolve({ data: { id: 1, type: 'post' } }); @@ -849,14 +900,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('A hasMany relationship can be reloaded if it was fetched via a link', async function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - const Post = store.modelFor('post'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - Post.reopen({ - comments: hasMany('comment', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.findRecord = function (store, type, id, snapshot) { assert.strictEqual(type, Post, 'find type was Post'); @@ -916,14 +979,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - const Post = store.modelFor('post'); - - Post.reopen({ - comments: hasMany('comment', { async: false }), - }); - adapter.findRecord = function (store, type, id, snapshot) { - assert.strictEqual(type, Post, 'find type was Post'); + assert.strictEqual(type, store.modelFor('post'), 'find type was Post'); assert.strictEqual(id, '1', 'find id was 1'); return resolve({ @@ -987,14 +1044,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('A hasMany relationship can be reloaded if it was fetched via ids', function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - const Post = store.modelFor('post'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - Post.reopen({ - comments: hasMany('comment', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.findRecord = function (store, type, id, snapshot) { assert.strictEqual(type, Post, 'find type was Post'); @@ -1054,14 +1123,27 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('A hasMany relationship can be reloaded even if it failed at the first time', async function (assert) { assert.expect(7); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); - adapter.findRecord = function () { return resolve({ data: { @@ -1123,14 +1205,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('A hasMany relationship can be directly reloaded if it was fetched via links', function (assert) { assert.expect(6); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - let Post = store.modelFor('post'); - - Post.reopen({ - comments: hasMany('comment', { async: true }), - }); adapter.findRecord = function (store, type, id) { assert.strictEqual(type, Post, 'find type was Post'); @@ -1175,16 +1269,29 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Has many via links - Calling reload multiple times does not send a new request if the first one is not settled', function (assert) { assert.expect(1); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let done = assert.async(); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); - adapter.findRecord = function (store, type, id) { return resolve({ data: { @@ -1226,13 +1333,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('A hasMany relationship can be directly reloaded if it was fetched via ids', function (assert) { + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - let Post = store.modelFor('post'); - - Post.reopen({ - comments: hasMany('comment', { async: true }), - }); adapter.findRecord = function (store, type, id, snapshot) { assert.strictEqual(type, Post, 'find type was Post'); @@ -1279,16 +1399,29 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Has many via ids - Calling reload multiple times does not send a new request if the first one is not settled', function (assert) { assert.expect(1); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let done = assert.async(); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); - adapter.findRecord = function (store, type, id, snapshot) { return resolve({ data: { @@ -1335,14 +1468,27 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('PromiseArray proxies createRecord to its ManyArray once the hasMany is loaded', function (assert) { assert.expect(4); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); - adapter.findHasMany = function (store, snapshot, link, relationship) { return resolve({ data: [ @@ -1384,14 +1530,27 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('An updated `links` value should invalidate a relationship cache', async function (assert) { assert.expect(8); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); - adapter.findHasMany = function (store, snapshot, link, relationship) { assert.strictEqual(relationship.type, 'comment', 'relationship was passed correctly'); @@ -1506,13 +1665,16 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test("When a polymorphic hasMany relationship is accessed, the store can call multiple adapters' findMany or find methods if the records are not loaded", function (assert) { + class User extends Model { + @attr name; + @hasMany('message', { polymorphic: true, async: true, inverse: 'user' }) messages; + @hasMany('user', { inverse: null, async: false }) contacts; + } + this.owner.register('model:user', User); + let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('user').reopen({ - messages: hasMany('message', { polymorphic: true, async: true }), - }); - adapter.shouldBackgroundReloadRecord = () => false; adapter.findRecord = function (store, type, id, snapshot) { if (type === store.modelFor('post')) { @@ -1624,14 +1786,16 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Type can be inferred from the key of an async hasMany relationship', function (assert) { assert.expect(1); + class User extends Model { + @attr name; + @hasMany('message', { polymorphic: true, async: false, inverse: 'user' }) messages; + @hasMany({ async: true, inverse: null }) contacts; + } + this.owner.register('model:user', User); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('user').reopen({ - contacts: hasMany({ async: true }), - }); - adapter.findRecord = function (store, type, ids, snapshots) { return { data: { @@ -1679,14 +1843,16 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Polymorphic relationships work with a hasMany whose type is inferred', function (assert) { assert.expect(1); + class User extends Model { + @attr name; + @hasMany('message', { polymorphic: true, async: false, inverse: 'user' }) messages; + @hasMany({ async: false, polymorphic: true, inverse: null }) contacts; + } + this.owner.register('model:user', User); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('user').reopen({ - contacts: hasMany({ polymorphic: true, async: false }), - }); - adapter.findRecord = function (store, type, ids, snapshots) { return { data: { id: 1, type: 'user' } }; }; @@ -1731,17 +1897,33 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Polymorphic relationships with a hasMany is set up correctly on both sides', function (assert) { assert.expect(2); + class Contact extends Model { + @belongsTo('user', { async: false, inverse: null }) user; + @hasMany('post', { async: false, inverse: 'contact' }) posts; + } + class Email extends Contact { + @attr email; + } + class Phone extends Contact { + @attr number; + } + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false, inverse: 'messages' }) user; + } + class Post extends Message { + @attr title; + @hasMany('comment', { async: false, inverse: 'message' }) comments; + @belongsTo('contact', { async: false, polymorphic: true, inverse: 'posts' }) contact; + } + this.owner.register('model:contact', Contact); + this.owner.register('model:email', Email); + this.owner.register('model:phone', Phone); + this.owner.register('model:message', Message); + this.owner.register('model:post', Post); let store = this.owner.lookup('service:store'); - store.modelFor('contact').reopen({ - posts: hasMany('post', { async: false }), - }); - - store.modelFor('post').reopen({ - contact: belongsTo('contact', { polymorphic: true, async: false }), - }); - let email = store.createRecord('email'); let post = store.createRecord('post', { contact: email, @@ -1922,13 +2104,25 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('When a record is created on the client, its async hasMany arrays should be in a loaded state', function (assert) { assert.expect(4); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - let store = this.owner.lookup('service:store'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + let store = this.owner.lookup('service:store'); let post = store.createRecord('post'); assert.ok(get(post, 'isLoaded'), 'The post should have isLoaded flag'); @@ -1975,13 +2169,25 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('We can set records ASYNC HM relationship', function (assert) { assert.expect(1); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - let store = this.owner.lookup('service:store'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + let store = this.owner.lookup('service:store'); let post = store.createRecord('post'); run(function () { @@ -2033,16 +2239,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('dual non-async HM <-> BT', function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { inverse: 'post', async: false }), - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: false, inverse: 'post' }) comments; + } - store.modelFor('comment').reopen({ - post: belongsTo('post', { async: false }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, inverse: 'comments' }) post; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.createRecord = function (store, type, snapshot) { let serialized = snapshot.record.serialize(); @@ -2100,14 +2316,27 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('When an unloaded record is added to the hasMany, it gets fetched once the hasMany is accessed even if the hasMany has been already fetched', async function (assert) { assert.expect(6); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); - let findManyCalls = 0; let findRecordCalls = 0; @@ -2500,11 +2729,13 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('Rollbacking attributes for deleted record restores implicit relationship correctly when the belongsTo side has been deleted - async', function (assert) { - let store = this.owner.lookup('service:store'); + class Page extends Model { + @attr number; + @belongsTo('chapter', { async: true, inverse: 'pages' }) chapter; + } + this.owner.register('model:page', Page); - store.modelFor('page').reopen({ - chapter: belongsTo('chapter', { async: true }), - }); + let store = this.owner.lookup('service:store'); let chapter, page; @@ -2732,7 +2963,7 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( } testInDebug('Passing a model as type to hasMany should not work', function (assert) { - assert.expect(1); + assert.expect(2); assert.expectAssertion(() => { const User = Model.extend(); @@ -2741,18 +2972,30 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( users: hasMany(User, { async: false }), }); }, /The first argument to hasMany must be a string/); + + assert.expectDeprecation({ id: 'ember-data:deprecate-early-static' }); }); test('Relationship.clear removes all records correctly', async function (assert) { - let store = this.owner.lookup('service:store'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - store.modelFor('comment').reopen({ - post: belongsTo('post', { async: false }), - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: false, inverse: 'post' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { inverse: 'post', async: false }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, inverse: 'comments' }) post; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); const [post] = store.push({ data: [ @@ -2811,15 +3054,25 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('unloading a record with associated records does not prevent the store from tearing down', function (assert) { - let store = this.owner.lookup('service:store'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - store.modelFor('comment').reopen({ - post: belongsTo('post', { async: false }), - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: false, inverse: 'post' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { inverse: 'post', async: false }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, inverse: 'comments' }) post; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); let post; run(() => { @@ -2993,14 +3246,15 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('hasMany hasAnyRelationshipData async loaded', function (assert) { assert.expect(1); + class Chapter extends Model { + @attr title; + @hasMany('page', { async: true, inverse: 'chapter' }) pages; + } + this.owner.register('model:chapter', Chapter); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('chapter').reopen({ - pages: hasMany('pages', { async: true }), - }); - adapter.findRecord = function (store, type, id, snapshot) { return resolve({ data: { @@ -3061,14 +3315,15 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('hasMany hasAnyRelationshipData async not loaded', function (assert) { assert.expect(1); + class Chapter extends Model { + @attr title; + @hasMany('page', { async: true, inverse: 'chapter' }) pages; + } + this.owner.register('model:chapter', Chapter); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('chapter').reopen({ - pages: hasMany('pages', { async: true }), - }); - adapter.findRecord = function (store, type, id, snapshot) { return resolve({ data: { @@ -3118,13 +3373,13 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('hasMany hasAnyRelationshipData async created', function (assert) { assert.expect(2); + class Chapter extends Model { + @attr title; + @hasMany('page', { async: true, inverse: 'chapter' }) pages; + } + this.owner.register('model:chapter', Chapter); let store = this.owner.lookup('service:store'); - - store.modelFor('chapter').reopen({ - pages: hasMany('pages', { async: true }), - }); - let chapter = store.createRecord('chapter', { title: 'The Story Begins' }); let page = store.createRecord('page'); @@ -3359,17 +3614,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Related link should be fetched when no relationship data is present', function (assert) { assert.expect(3); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'post' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true, inverse: 'post' }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, inverse: 'comments' }) post; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); - store.modelFor('comment').reopen({ - post: belongsTo('post', { async: false, inverse: 'comments' }), - }); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.shouldBackgroundReloadRecord = () => { return false; @@ -3420,17 +3684,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Related link should take precedence over relationship data when local record data is missing', function (assert) { assert.expect(3); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'post' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true, inverse: 'post' }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, inverse: 'comments' }) post; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); - store.modelFor('comment').reopen({ - post: belongsTo('post', { async: false, inverse: 'comments' }), - }); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.shouldBackgroundReloadRecord = () => { return false; @@ -3482,17 +3755,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Local relationship data should take precedence over related link when local record data is available', function (assert) { assert.expect(1); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'post' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true, inverse: 'post' }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, inverse: 'comments' }) post; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); - store.modelFor('comment').reopen({ - post: belongsTo('post', { async: false, inverse: 'comments' }), - }); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.shouldBackgroundReloadRecord = () => { return false; @@ -3541,17 +3823,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Related link should take precedence over local record data when relationship data is not initially available', function (assert) { assert.expect(3); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'post' }) comments; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true, inverse: 'post' }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, inverse: 'comments' }) post; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); - store.modelFor('comment').reopen({ - post: belongsTo('post', { async: false, inverse: 'comments' }), - }); + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); adapter.shouldBackgroundReloadRecord = () => { return false; @@ -3623,14 +3914,27 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('Updated related link should take precedence over relationship data and local record data', function (assert) { assert.expect(3); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); - adapter.findHasMany = function (store, snapshot, url, relationship) { assert.strictEqual(url, 'comments-updated-link', 'url is correct'); assert.ok(true, "The adapter's findHasMany method should be called"); @@ -3681,14 +3985,27 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( test('PromiseArray proxies createRecord to its ManyArray before the hasMany is loaded', function (assert) { assert.expect(1); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } + + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } + + class Comment extends Message { + @attr body; + @belongsTo('post', { async: false, polymorphic: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); - adapter.findHasMany = function (store, record, link, relationship) { return resolve({ data: [ @@ -3722,15 +4039,27 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('deleteRecord + unloadRecord', async function (assert) { - let store = this.owner.lookup('service:store'); + class User extends Model { + @attr name; + @hasMany('message', { polymorphic: true, async: false, inverse: 'user' }) messages; + @hasMany('user', { inverse: null, async: false }) contacts; + @hasMany('post', { async: true, inverse: null }) posts; + } + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false, inverse: 'messages' }) user; + } - store.modelFor('user').reopen({ - posts: hasMany('post', { inverse: null }), - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: false, inverse: 'message' }) comments; + @belongsTo('user', { inverse: null, async: false }) user; + } + this.owner.register('model:user', User); + this.owner.register('model:message', Message); + this.owner.register('model:post', Post); - store.modelFor('post').reopen({ - user: belongsTo('user', { inverse: null, async: false }), - }); + let store = this.owner.lookup('service:store'); store.push({ data: [ @@ -3966,16 +4295,26 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( }); test('A hasMany relationship with a link will trigger the link request even if a inverse related object is pushed to the store', function (assert) { - let store = this.owner.lookup('service:store'); - let adapter = store.adapterFor('application'); + class Message extends Model { + @attr('date') created_at; + @belongsTo('user', { async: false }) iser; + } - store.modelFor('post').reopen({ - comments: hasMany('comment', { async: true }), - }); + class Post extends Message { + @attr title; + @hasMany('comment', { async: true, inverse: 'message' }) comments; + } - store.modelFor('comment').reopen({ - message: belongsTo('post', { async: true }), - }); + class Comment extends Message { + @attr body; + @belongsTo('post', { async: true, inverse: 'comments' }) message; + } + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:message', Message); + + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); const postID = '1'; diff --git a/packages/-ember-data/tests/integration/serializers/json-api-serializer-test.js b/packages/-ember-data/tests/integration/serializers/json-api-serializer-test.js index bf388806c26..d0a53a8a0e8 100644 --- a/packages/-ember-data/tests/integration/serializers/json-api-serializer-test.js +++ b/packages/-ember-data/tests/integration/serializers/json-api-serializer-test.js @@ -388,25 +388,32 @@ module('integration/serializers/json-api-serializer - JSONAPISerializer', functi test('options are passed to transform for serialization', function (assert) { assert.expect(1); + const User = DS.Model.extend({ + firstName: DS.attr('string'), + lastName: DS.attr('string'), + title: DS.attr('string'), + handles: DS.hasMany('handle', { async: true, polymorphic: true }), + company: DS.belongsTo('company', { async: true }), + reportsTo: DS.belongsTo('user', { async: true, inverse: null }), + myCustomField: DS.attr('custom', { + custom: 'config', + }), + }); + + this.owner.register('model:user', User); + let store = this.owner.lookup('service:store'); + let user = store.createRecord('user', { myCustomField: 'value' }); this.owner.register( 'transform:custom', DS.Transform.extend({ serialize: function (deserialized, options) { - assert.deepEqual(options, { custom: 'config' }); + assert.deepEqual(options, { custom: 'config' }, 'we have the right options'); }, }) ); - store.modelFor('user').reopen({ - myCustomField: DS.attr('custom', { - custom: 'config', - }), - }); - - let user = store.createRecord('user', { myCustomField: 'value' }); - store.serializerFor('user').serialize(user._createSnapshot()); }); diff --git a/packages/-ember-data/tests/integration/serializers/json-serializer-test.js b/packages/-ember-data/tests/integration/serializers/json-serializer-test.js index 4f14354c192..4366eb6c821 100644 --- a/packages/-ember-data/tests/integration/serializers/json-serializer-test.js +++ b/packages/-ember-data/tests/integration/serializers/json-serializer-test.js @@ -6,33 +6,18 @@ import { underscore } from '@ember/string'; import { module, test } from 'qunit'; -import DS from 'ember-data'; import { setupTest } from 'ember-qunit'; +import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import JSONSerializer from '@ember-data/serializer/json'; +import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest'; +import Transform from '@ember-data/serializer/transform'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; -var Post, Comment, Favorite; - module('integration/serializer/json - JSONSerializer', function (hooks) { setupTest(hooks); hooks.beforeEach(function () { - Post = DS.Model.extend({ - title: DS.attr('string'), - comments: DS.hasMany('comment', { inverse: null, async: false }), - }); - Comment = DS.Model.extend({ - body: DS.attr('string'), - post: DS.belongsTo('post', { inverse: null, async: false }), - }); - Favorite = DS.Model.extend({ - post: DS.belongsTo('post', { inverse: null, async: true, polymorphic: true }), - }); - - this.owner.register('model:post', Post); - this.owner.register('model:comment', Comment); - this.owner.register('model:favorite', Favorite); this.owner.register('serializer:application', JSONSerializer.extend()); }); @@ -40,6 +25,18 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: false }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let post = store.createRecord('post', { title: 'Rails is omakase', comments: [], @@ -55,6 +52,19 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test("serialize doesn't include relationship if not aware of one", function (assert) { let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); + + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: false }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let post = store.createRecord('post', { title: 'Rails is omakase' }); let json = serializer.serialize(post._createSnapshot()); @@ -66,6 +76,19 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('serialize includes id when includeId is true', function (assert) { let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); + + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: false }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let post = store.createRecord('post', { title: 'Rails is omakase', comments: [] }); run(() => { @@ -82,6 +105,18 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializeAttribute', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: false }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); let post = store.createRecord('post', { title: 'Rails is omakase' }); @@ -95,9 +130,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializeAttribute respects keyForAttribute', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: false }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ keyForAttribute(key) { return key.toUpperCase(); }, @@ -114,6 +161,18 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializeBelongsTo', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: false }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); let post = store.createRecord('post', { title: 'Rails is omakase', id: '1' }); @@ -126,6 +185,18 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializeBelongsTo with null', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: false }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); let comment = store.createRecord('comment', { body: 'Omakase is delicious', post: null }); @@ -143,9 +214,17 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('async serializeBelongsTo with null', function (assert) { - Comment.reopen({ - post: DS.belongsTo('post', { async: true }), - }); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); @@ -164,9 +243,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializeBelongsTo respects keyForRelationship', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ keyForRelationship(key, type) { return key.toUpperCase(); }, @@ -186,9 +277,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializeHasMany respects keyForRelationship', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ keyForRelationship(key, type) { return key.toUpperCase(); }, @@ -217,6 +320,18 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializeHasMany omits unknown relationships on pushed record', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let store = this.owner.lookup('service:store'); let post = run(() => @@ -238,6 +353,18 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('shouldSerializeHasMany', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let store = this.owner.lookup('service:store'); let post = store.createRecord('post', { title: 'Rails is omakase', id: '1' }); store.createRecord('comment', { body: 'Omakase is delicious', post: post, id: '1' }); @@ -252,12 +379,24 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializeIntoHash', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('application'); let post = store.createRecord('post', { title: 'Rails is omakase', comments: [] }); let json = {}; - serializer.serializeIntoHash(json, Post, post._createSnapshot()); + serializer.serializeIntoHash(json, store.modelFor('post'), post._createSnapshot()); assert.deepEqual(json, { title: 'Rails is omakase', @@ -266,11 +405,23 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('serializePolymorphicType sync', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + assert.expect(1); this.owner.register( 'serializer:comment', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ serializePolymorphicType(record, json, relationship) { let key = relationship.key; let belongsTo = record.belongsTo(key); @@ -292,14 +443,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('serializePolymorphicType async', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } - Comment.reopen({ - post: DS.belongsTo('post', { async: true }), - }); + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:comment', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ serializePolymorphicType(record, json, relationship) { assert.ok(true, 'serializePolymorphicType is called when serialize a polymorphic belongsTo'); }, @@ -316,6 +474,18 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('normalizeResponse normalizes each record in the array', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + var postNormalizeCount = 0; var posts = [ { id: '1', title: 'Rails is omakase' }, @@ -324,7 +494,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ normalize() { postNormalizeCount++; return this._super.apply(this, arguments); @@ -335,16 +505,28 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let store = this.owner.lookup('service:store'); run(function () { - store.serializerFor('post').normalizeResponse(store, Post, posts, null, 'findAll'); + store.serializerFor('post').normalizeResponse(store, store.modelFor('post'), posts, null, 'findAll'); }); assert.strictEqual(postNormalizeCount, 2, 'two posts are normalized'); }); test('Serializer should respect the attrs hash when extracting records', function (assert) { + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { title: 'title_payload_key', comments: { key: 'my_comments' }, @@ -359,7 +541,9 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }; let store = this.owner.lookup('service:store'); - var post = store.serializerFor('post').normalizeResponse(store, Post, jsonHash, '1', 'findRecord'); + var post = store + .serializerFor('post') + .normalizeResponse(store, store.modelFor('post'), jsonHash, '1', 'findRecord'); assert.strictEqual(post.data.attributes.title, 'Rails is omakase'); assert.deepEqual(post.data.relationships.comments.data, [ @@ -369,15 +553,15 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('Serializer should map `attrs` attributes directly when keyForAttribute also has a transform', function (assert) { - const Post = DS.Model.extend({ - authorName: DS.attr('string'), + const Post = Model.extend({ + authorName: attr('string'), }); this.owner.register('model:post', Post); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ keyForAttribute: underscore, attrs: { authorName: 'author_name_key', @@ -392,18 +576,30 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let store = this.owner.lookup('service:store'); - var post = store.serializerFor('post').normalizeResponse(store, Post, jsonHash, '1', 'findRecord'); + var post = store + .serializerFor('post') + .normalizeResponse(store, store.modelFor('post'), jsonHash, '1', 'findRecord'); assert.strictEqual(post.data.attributes.authorName, 'DHH'); }); test('Serializer should respect the attrs hash when serializing records', function (assert) { - Post.reopen({ - parentPost: DS.belongsTo('post', { inverse: null, async: true }), - }); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: true }) comments; + @belongsTo('post', { inverse: null, async: true }) parentPost; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { title: 'title_payload_key', parentPost: { key: 'my_parent' }, @@ -435,10 +631,10 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('Serializer respects if embedded model has an attribute named "type" - #3726', function (assert) { - this.owner.register('serializer:child', DS.JSONSerializer); + this.owner.register('serializer:child', JSONSerializer); this.owner.register( 'serializer:parent', - DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { + JSONSerializer.extend(EmbeddedRecordsMixin, { attrs: { child: { embedded: 'always' }, }, @@ -446,14 +642,14 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { ); this.owner.register( 'model:parent', - DS.Model.extend({ - child: DS.belongsTo('child'), + Model.extend({ + child: belongsTo('child'), }) ); this.owner.register( 'model:child', - DS.Model.extend({ - type: DS.attr(), + Model.extend({ + type: attr(), }) ); @@ -481,10 +677,10 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('Serializer respects if embedded model has a relationship named "type" - #3726', function (assert) { - this.owner.register('serializer:child', DS.JSONSerializer); + this.owner.register('serializer:child', JSONSerializer); this.owner.register( 'serializer:parent', - DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { + JSONSerializer.extend(EmbeddedRecordsMixin, { attrs: { child: { embedded: 'always' }, }, @@ -492,17 +688,17 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { ); this.owner.register( 'model:parent', - DS.Model.extend({ - child: DS.belongsTo('child'), + Model.extend({ + child: belongsTo('child'), }) ); this.owner.register( 'model:child', - DS.Model.extend({ - type: DS.belongsTo('le-type'), + Model.extend({ + type: belongsTo('le-type'), }) ); - this.owner.register('model:le-type', DS.Model.extend()); + this.owner.register('model:le-type', Model.extend()); var jsonHash = { id: 1, @@ -534,10 +730,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('Serializer respects `serialize: false` on the attrs hash', function (assert) { assert.expect(2); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { title: { serialize: false }, }, @@ -554,10 +761,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('Serializer respects `serialize: false` on the attrs hash for a `hasMany` property', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { comments: { serialize: false }, }, @@ -577,10 +795,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('Serializer respects `serialize: false` on the attrs hash for a `belongsTo` property', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:comment', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { post: { serialize: false }, }, @@ -600,10 +829,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('Serializer respects `serialize: false` on the attrs hash for a `hasMany` property', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { comments: { serialize: false }, }, @@ -623,10 +863,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('Serializer respects `serialize: false` on the attrs hash for a `belongsTo` property', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:comment', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { post: { serialize: false }, }, @@ -646,10 +897,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('Serializer respects `serialize: true` on the attrs hash for a `hasMany` property', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { comments: { serialize: true }, }, @@ -673,10 +935,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('Serializer respects `serialize: true` on the attrs hash for a `belongsTo` property', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:comment', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { post: { serialize: true }, }, @@ -696,11 +969,21 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { test('Serializer should merge attrs from superclasses', function (assert) { assert.expect(4); - Post.reopen({ - description: DS.attr('string'), - anotherString: DS.attr('string'), - }); - var BaseSerializer = DS.JSONSerializer.extend({ + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + + var BaseSerializer = JSONSerializer.extend({ attrs: { title: 'title_payload_key', anotherString: 'base_another_string_key', @@ -731,25 +1014,54 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('Serializer should respect the primaryKey attribute when extracting records', function (assert) { + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ primaryKey: '_ID_', }) ); let jsonHash = { _ID_: 1, title: 'Rails is omakase' }; let store = this.owner.lookup('service:store'); - let post = store.serializerFor('post').normalizeResponse(store, Post, jsonHash, '1', 'findRecord'); + let post = store + .serializerFor('post') + .normalizeResponse(store, store.modelFor('post'), jsonHash, '1', 'findRecord'); assert.strictEqual(post.data.id, '1'); assert.strictEqual(post.data.attributes.title, 'Rails is omakase'); }); test('Serializer should respect the primaryKey attribute when serializing records', function (assert) { + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ primaryKey: '_ID_', }) ); @@ -762,9 +1074,22 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('Serializer should respect keyForAttribute when extracting records', function (assert) { + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ keyForAttribute(key) { return key.toUpperCase(); }, @@ -773,16 +1098,29 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let jsonHash = { id: 1, TITLE: 'Rails is omakase' }; let store = this.owner.lookup('service:store'); - let post = store.serializerFor('post').normalize(Post, jsonHash); + let post = store.serializerFor('post').normalize(store.modelFor('post'), jsonHash); assert.strictEqual(post.data.id, '1'); assert.strictEqual(post.data.attributes.title, 'Rails is omakase'); }); test('Serializer should respect keyForRelationship when extracting records', function (assert) { + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ keyForRelationship(key, type) { return key.toUpperCase(); }, @@ -791,21 +1129,38 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let jsonHash = { id: 1, title: 'Rails is omakase', COMMENTS: ['1'] }; let store = this.owner.lookup('service:store'); - let post = store.serializerFor('post').normalize(Post, jsonHash); + let post = store.serializerFor('post').normalize(store.modelFor('post'), jsonHash); assert.deepEqual(post.data.relationships.comments.data, [{ id: '1', type: 'comment' }]); }); test('Calling normalize should normalize the payload (only the passed keys)', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } - var Person = DS.Model.extend({ - posts: DS.hasMany('post', { async: false }), - }); + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { notInHash: 'aCustomAttrNotInHash', inHash: 'aCustomAttrInHash', @@ -813,15 +1168,6 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }) ); - this.owner.register('model:person', Person); - - Post.reopen({ - content: DS.attr('string'), - author: DS.belongsTo('person', { async: false }), - notInHash: DS.attr('string'), - inHash: DS.attr('string'), - }); - let store = this.owner.lookup('service:store'); var normalizedPayload = store.serializerFor('post').normalize(store.modelFor('post'), { @@ -852,9 +1198,25 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { var json = {}; var expected = { post: '1', postTYPE: 'post' }; + class Post extends Model { + @attr('string') title; + @hasMany('comment', { inverse: null, async: false }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: false }) post; + } + class Favorite extends Model { + @belongsTo('post', { inverse: null, async: true, polymorphic: true }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:favorite', Favorite); + this.owner.register( 'serializer:favorite', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ serializePolymorphicType(snapshot, json, relationship) { var key = relationship.key; json[key + 'TYPE'] = snapshot.belongsTo(key).modelName; @@ -875,9 +1237,30 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('extractErrors respects custom key mappings', function (assert) { + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { title: 'le_title', comments: { key: 'my_comments' }, @@ -899,7 +1282,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }; let store = this.owner.lookup('service:store'); - var errors = store.serializerFor('post').extractErrors(store, Post, payload); + var errors = store.serializerFor('post').extractErrors(store, store.modelFor('post'), payload); assert.deepEqual(errors, { title: ['title errors'], @@ -908,7 +1291,28 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('extractErrors expects error information located on the errors property of payload', function (assert) { - this.owner.register('serializer:post', DS.JSONSerializer.extend()); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); + this.owner.register('serializer:post', JSONSerializer.extend()); var payload = { attributeWhichWillBeRemovedinExtractErrors: ['true'], @@ -921,28 +1325,70 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }; let store = this.owner.lookup('service:store'); - var errors = store.serializerFor('post').extractErrors(store, Post, payload); + var errors = store.serializerFor('post').extractErrors(store, store.modelFor('post'), payload); assert.deepEqual(errors, { title: ['title errors'] }); }); test('extractErrors leaves payload untouched if it has no errors property', function (assert) { - this.owner.register('serializer:post', DS.JSONSerializer.extend()); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); + this.owner.register('serializer:post', JSONSerializer.extend()); var payload = { untouchedSinceNoErrorsSiblingPresent: ['true'], }; let store = this.owner.lookup('service:store'); - var errors = store.serializerFor('post').extractErrors(store, Post, payload); + var errors = store.serializerFor('post').extractErrors(store, store.modelFor('post'), payload); assert.deepEqual(errors, { untouchedSinceNoErrorsSiblingPresent: ['true'] }); }); test('normalizeResponse should extract meta using extractMeta', function (assert) { + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ extractMeta(store, modelClass, payload) { let meta = this._super(...arguments); meta.authors.push('Tomhuda'); @@ -961,13 +1407,36 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }; let store = this.owner.lookup('service:store'); - var post = store.serializerFor('post').normalizeResponse(store, Post, jsonHash, '1', 'findRecord'); + var post = store + .serializerFor('post') + .normalizeResponse(store, store.modelFor('post'), jsonHash, '1', 'findRecord'); assert.deepEqual(post.meta.authors, ['Tomster', 'Tomhuda']); }); test('normalizeResponse returns empty `included` payload by default', function (assert) { - this.owner.register('serializer:post', DS.JSONSerializer.extend()); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); + this.owner.register('serializer:post', JSONSerializer.extend()); var jsonHash = { id: '1', @@ -975,13 +1444,36 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }; let store = this.owner.lookup('service:store'); - var post = store.serializerFor('post').normalizeResponse(store, Post, jsonHash, '1', 'findRecord'); + var post = store + .serializerFor('post') + .normalizeResponse(store, store.modelFor('post'), jsonHash, '1', 'findRecord'); assert.deepEqual(post.included, []); }); test('normalizeResponse returns empty `included` payload when relationship is undefined', function (assert) { - this.owner.register('serializer:post', DS.JSONSerializer.extend()); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); + this.owner.register('serializer:post', JSONSerializer.extend()); var jsonHash = { id: '1', @@ -990,16 +1482,39 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }; let store = this.owner.lookup('service:store'); - var post = store.serializerFor('post').normalizeResponse(store, Post, jsonHash, '1', 'findRecord'); + var post = store + .serializerFor('post') + .normalizeResponse(store, store.modelFor('post'), jsonHash, '1', 'findRecord'); assert.deepEqual(post.included, []); }); test('normalizeResponse respects `included` items (single response)', function (assert) { - this.owner.register('serializer:comment', DS.JSONSerializer); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); + this.owner.register('serializer:comment', JSONSerializer); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { + JSONSerializer.extend(EmbeddedRecordsMixin, { attrs: { comments: { embedded: 'always' }, }, @@ -1016,7 +1531,9 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }; let store = this.owner.lookup('service:store'); - var post = store.serializerFor('post').normalizeResponse(store, Post, jsonHash, '1', 'findRecord'); + var post = store + .serializerFor('post') + .normalizeResponse(store, store.modelFor('post'), jsonHash, '1', 'findRecord'); assert.deepEqual(post.included, [ { id: '1', type: 'comment', attributes: { body: 'comment 1' }, relationships: {} }, @@ -1025,10 +1542,31 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); test('normalizeResponse respects `included` items (array response)', function (assert) { - this.owner.register('serializer:comment', DS.JSONSerializer); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); + this.owner.register('serializer:comment', JSONSerializer); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, { + JSONSerializer.extend(EmbeddedRecordsMixin, { attrs: { comments: { embedded: 'always' }, }, @@ -1052,7 +1590,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { ]; let store = this.owner.lookup('service:store'); - var post = store.serializerFor('post').normalizeResponse(store, Post, payload, '1', 'findAll'); + var post = store.serializerFor('post').normalizeResponse(store, store.modelFor('post'), payload, '1', 'findAll'); assert.deepEqual(post.included, [ { id: '1', type: 'comment', attributes: { body: 'comment 1' }, relationships: {} }, @@ -1062,9 +1600,30 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }); testInDebug('normalizeResponse ignores unmapped attributes', function (assert) { + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { title: { serialize: false }, notInMapping: { serialize: false }, @@ -1081,29 +1640,47 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { let store = this.owner.lookup('service:store'); assert.expectWarning(function () { - var post = store.serializerFor('post').normalizeResponse(store, Post, jsonHash, '1', 'findRecord'); + var post = store + .serializerFor('post') + .normalizeResponse(store, store.modelFor('post'), jsonHash, '1', 'findRecord'); assert.strictEqual(post.data.attributes.title, 'Rails is omakase'); }, /There is no attribute or relationship with the name/); }); - test('options are passed to transform for serialization', function (assert) { + test('options are passed to transform for serialization via createSnapshot', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @attr('custom', { custom: 'config' }) custom; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); this.owner.register( 'transform:custom', - DS.Transform.extend({ + Transform.extend({ serialize: function (deserialized, options) { assert.deepEqual(options, { custom: 'config' }); }, }) ); - Post.reopen({ - custom: DS.attr('custom', { - custom: 'config', - }), - }); - let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('post'); let post = store.createRecord('post', { custom: 'value' }); @@ -1111,36 +1688,74 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { serializer.serialize(post._createSnapshot()); }); - test('options are passed to transform for normalization', function (assert) { + test('options are passed to transform for normalization via serializer.normalize', function (assert) { assert.expect(1); + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @attr('custom', { custom: 'config' }) custom; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); this.owner.register( 'transform:custom', - DS.Transform.extend({ + Transform.extend({ deserialize: function (serialized, options) { assert.deepEqual(options, { custom: 'config' }); }, }) ); - Post.reopen({ - custom: DS.attr('custom', { - custom: 'config', - }), - }); - let store = this.owner.lookup('service:store'); let serializer = store.serializerFor('post'); - serializer.normalize(Post, { + serializer.normalize(store.modelFor('post'), { custom: 'value', }); }); test('Serializer should respect the attrs hash in links', function (assert) { + class Post extends Model { + @attr('string') title; + @attr('string') description; + @attr('string') anotherString; + @attr('string') content; + @attr('string') inHash; + @attr('string') notInHash; + @belongsTo('person', { async: false, inverse: 'posts' }) author; + @hasMany('comment', { inverse: null, async: true }) comments; + } + class Comment extends Model { + @attr('string') body; + @belongsTo('post', { inverse: null, async: true }) post; + } + class Person extends Model { + @hasMany('post', { async: false, inverse: 'author' }) posts; + } + + this.owner.register('model:post', Post); + this.owner.register('model:comment', Comment); + this.owner.register('model:person', Person); + this.owner.register( 'serializer:post', - DS.JSONSerializer.extend({ + JSONSerializer.extend({ attrs: { title: 'title_payload_key', comments: { key: 'my_comments' }, @@ -1156,7 +1771,7 @@ module('integration/serializer/json - JSONSerializer', function (hooks) { }; let store = this.owner.lookup('service:store'); - var post = this.owner.lookup('serializer:post').normalizeSingleResponse(store, Post, jsonHash); + var post = this.owner.lookup('serializer:post').normalizeSingleResponse(store, store.modelFor('post'), jsonHash); assert.strictEqual(post.data.attributes.title, 'Rails is omakase'); assert.strictEqual(post.data.relationships.comments.links.related, 'posts/1/comments'); diff --git a/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js b/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js index d1f8665c024..77c329ac3d8 100644 --- a/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js +++ b/packages/-ember-data/tests/integration/serializers/rest-serializer-test.js @@ -732,16 +732,35 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { }); test('normalizeResponse with async polymorphic hasMany', function (assert) { - SuperVillain.reopen({ - evilMinions: DS.hasMany('evil-minion', { async: true, polymorphic: true }), + const HomePlanet = DS.Model.extend({ + name: DS.attr('string'), + superVillains: DS.hasMany('super-villain2', { async: false }), }); + const SuperVillain = DS.Model.extend({ + firstName: DS.attr('string'), + lastName: DS.attr('string'), + homePlanet: DS.belongsTo('home-planet2', { async: false }), + evilMinions: DS.hasMany('evil-minion2', { async: true, polymorphic: true }), + }); + const EvilMinion = DS.Model.extend({ + superVillain: DS.belongsTo('super-villain2', { async: false }), + name: DS.attr('string'), + }); + const YellowMinion = EvilMinion.extend({ + eyes: DS.attr('number'), + }); + + this.owner.register('model:super-villain2', SuperVillain); + this.owner.register('model:home-planet2', HomePlanet); + this.owner.register('model:evil-minion2', EvilMinion); + this.owner.register('model:yellow-minion2', YellowMinion); let store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); adapter.findRecord = () => { return { - superVillains: [ + superVillain2s: [ { id: '1', firstName: 'Yehuda', @@ -756,10 +775,10 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { adapter.findHasMany = () => { return { - evilMinion: [ + evilMinion2: [ { id: 1, - type: 'yellowMinion', + type: 'yellowMinion2', name: 'Alex', eyes: 3, }, @@ -769,13 +788,13 @@ module('integration/serializer/rest - RESTSerializer', function (hooks) { run(function () { store - .findRecord('super-villain', 1) + .findRecord('super-villain2', '1') .then((superVillain) => { return superVillain.get('evilMinions'); }) .then((evilMinions) => { - assert.ok(evilMinions.get('firstObject') instanceof YellowMinion); - assert.strictEqual(evilMinions.get('firstObject.eyes'), 3); + assert.ok(evilMinions.get('firstObject') instanceof YellowMinion, 'we have an instance'); + assert.strictEqual(evilMinions.get('firstObject.eyes'), 3, 'we have the right minion'); }); }); }); diff --git a/packages/-ember-data/tests/integration/store-test.js b/packages/-ember-data/tests/integration/store-test.js index f90d003469b..c3e85526c7d 100644 --- a/packages/-ember-data/tests/integration/store-test.js +++ b/packages/-ember-data/tests/integration/store-test.js @@ -9,34 +9,31 @@ import { setupTest } from 'ember-qunit'; import JSONAPIAdapter from '@ember-data/adapter/json-api'; import RESTAdapter from '@ember-data/adapter/rest'; +import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import { DEPRECATE_RSVP_PROMISE } from '@ember-data/private-build-infra/deprecations'; import JSONAPISerializer from '@ember-data/serializer/json-api'; import RESTSerializer from '@ember-data/serializer/rest'; import deepCopy from '@ember-data/unpublished-test-infra/test-support/deep-copy'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; -const Person = DS.Model.extend({ - name: DS.attr('string'), - cars: DS.hasMany('car', { async: false }), -}); +class Person extends Model { + @attr('string') name; + @hasMany('car', { async: false }) cars; -Person.reopenClass({ - toString() { + static toString() { return 'Person'; - }, -}); + } +} -const Car = DS.Model.extend({ - make: DS.attr('string'), - model: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), -}); +class Car extends Model { + @attr('string') make; + @attr('string') model; + @belongsTo('person', { async: false }) person; -Car.reopenClass({ - toString() { + static toString() { return 'Car'; - }, -}); + } +} function ajaxResponse(value) { return function (url, verb, hash) { diff --git a/packages/-ember-data/tests/unit/many-array-test.js b/packages/-ember-data/tests/unit/many-array-test.js index 6497c1a99a3..60e55415539 100644 --- a/packages/-ember-data/tests/unit/many-array-test.js +++ b/packages/-ember-data/tests/unit/many-array-test.js @@ -4,54 +4,35 @@ import { module, test } from 'qunit'; import { resolve } from 'rsvp'; import { gte } from 'ember-compatibility-helpers'; -import DS from 'ember-data'; import { setupTest } from 'ember-qunit'; -let store, Post, Tag; +import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; +import { ManyArray } from '@ember-data/model/-private'; -const { attr, hasMany, belongsTo } = DS; - -module('unit/many_array - DS.ManyArray', function (hooks) { +module('unit/many_array - ManyArray', function (hooks) { setupTest(hooks); - hooks.beforeEach(function () { - Post = DS.Model.extend({ - title: attr('string'), - tags: hasMany('tag', { async: false }), - }); - - Post.reopenClass({ - toString() { - return 'Post'; - }, - }); - - Tag = DS.Model.extend({ - name: attr('string'), - post: belongsTo('post', { async: false }), - }); - - Tag.reopenClass({ - toString() { - return 'Tag'; - }, - }); - - this.owner.register('model:post', Post); - this.owner.register('model:tag', Tag); - - store = this.owner.lookup('service:store'); - }); - test('manyArray.save() calls save() on all records', function (assert) { assert.expect(3); - Tag.reopen({ + class Post extends Model { + @attr('string') title; + @hasMany('tag', { async: false }) tags; + } + + class Tag extends Model { + @attr('string') name; + @belongsTo('post', { async: false }) post; + save() { assert.ok(true, 'record.save() was called'); return resolve(); - }, - }); + } + } + + this.owner.register('model:post', Post); + this.owner.register('model:tag', Tag); + const store = this.owner.lookup('service:store'); return run(() => { store.push({ @@ -102,8 +83,21 @@ module('unit/many_array - DS.ManyArray', function (hooks) { if (!gte('4.0.0')) { test('manyArray trigger arrayContentChange functions with the correct values', function (assert) { assert.expect(6); + class Post extends Model { + @attr('string') title; + @hasMany('tag', { async: false }) tags; + } + + class Tag extends Model { + @attr('string') name; + @belongsTo('post', { async: false }) post; + } + + this.owner.register('model:post', Post); + this.owner.register('model:tag', Tag); + const store = this.owner.lookup('service:store'); - const TestManyArray = DS.ManyArray.proto(); + const TestManyArray = ManyArray.proto(); let willChangeStartIdx; let willChangeRemoveAmt; @@ -113,7 +107,7 @@ module('unit/many_array - DS.ManyArray', function (hooks) { let originalArrayContentDidChange = TestManyArray.arrayContentDidChange; let originalInit = TestManyArray.init; - // override DS.ManyArray temp (cleanup occures in afterTest); + // override ManyArray temp (cleanup occures in afterTest); TestManyArray.init = function (...args) { // We aren't actually adding any observers in this test diff --git a/packages/-ember-data/tests/unit/model/relationships/has-many-test.js b/packages/-ember-data/tests/unit/model/relationships/has-many-test.js index 78625fc1007..bc5b2e4bdd1 100644 --- a/packages/-ember-data/tests/unit/model/relationships/has-many-test.js +++ b/packages/-ember-data/tests/unit/model/relationships/has-many-test.js @@ -5,38 +5,40 @@ import { settled } from '@ember/test-helpers'; import { module, test } from 'qunit'; import { hash, Promise as EmberPromise } from 'rsvp'; -import DS from 'ember-data'; import { setupTest } from 'ember-qunit'; +import Adapter from '@ember-data/adapter'; import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; +import { PromiseManyArray } from '@ember-data/model/-private'; +import JSONAPISerializer from '@ember-data/serializer/json-api'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; import todo from '@ember-data/unpublished-test-infra/test-support/todo'; -module('unit/model/relationships - DS.hasMany', function (hooks) { +module('unit/model/relationships - hasMany', function (hooks) { setupTest(hooks); hooks.beforeEach(function () { - this.owner.register('adapter:application', DS.Adapter.extend()); - this.owner.register('serializer:application', DS.JSONAPISerializer.extend()); + this.owner.register('adapter:application', Adapter.extend()); + this.owner.register('serializer:application', JSONAPISerializer.extend()); }); test('hasMany handles pre-loaded relationships', function (assert) { assert.expect(13); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Pet = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Pet = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: false }), - pets: DS.hasMany('pet', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: false }), + pets: hasMany('pet', { async: false }), }); this.owner.register('model:tag', Tag); @@ -240,15 +242,15 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('hasMany does not notify when it is initially reified', function (assert) { assert.expect(1); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person', { async: false }), }); Tag.toString = () => 'Tag'; - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); Person.toString = () => 'Person'; @@ -307,14 +309,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('hasMany can be initially reified with null', function (assert) { assert.expect(1); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -352,14 +354,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('hasMany with explicit initial null works even when the inverse was set to not null', function (assert) { assert.expect(2); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -438,27 +440,23 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('hasMany with duplicates from payload', function (assert) { assert.expect(1); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person', { async: false }), }); - Tag.reopenClass({ - toString() { - return 'tag'; - }, - }); + Tag.toString = () => { + return 'tag'; + }; - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); - Person.reopenClass({ - toString() { - return 'person'; - }, - }); + Person.toString = () => { + return 'person'; + }; this.owner.register('model:tag', Tag); this.owner.register('model:person', Person); @@ -518,27 +516,23 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('many2many loads both sides #5140', function (assert) { assert.expect(3); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person', { async: false }), }); - Tag.reopenClass({ - toString() { - return 'tag'; - }, - }); + Tag.toString = () => { + return 'tag'; + }; - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tags', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tags', { async: false }), }); - Person.reopenClass({ - toString() { - return 'person'; - }, - }); + Person.toString = () => { + return 'person'; + }; this.owner.register('model:tag', Tag); this.owner.register('model:person', Person); @@ -654,14 +648,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('hasMany with explicit null works even when the inverse was set to not null', function (assert) { assert.expect(3); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -749,9 +743,9 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('hasMany tolerates reflexive self-relationships', function (assert) { assert.expect(1); - const Person = DS.Model.extend({ - name: DS.attr(), - trueFriends: DS.hasMany('person', { async: false }), + const Person = Model.extend({ + name: attr(), + trueFriends: hasMany('person', { async: false }), }); this.owner.register('model:person', Person); @@ -794,20 +788,20 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('hasMany lazily loads async relationships', function (assert) { assert.expect(5); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Pet = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Pet = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: true }), - pets: DS.hasMany('pet', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: true }), + pets: hasMany('pet', { async: false }), }); this.owner.register('model:tag', Tag); @@ -931,10 +925,10 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); test('should be able to retrieve the type for a hasMany relationship without specifying a type from its metadata', function (assert) { - const Tag = DS.Model.extend({}); + const Tag = Model.extend({}); - const Person = DS.Model.extend({ - tags: DS.hasMany('tag', { async: false }), + const Person = Model.extend({ + tags: hasMany('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -950,10 +944,10 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); test('should be able to retrieve the type for a hasMany relationship specified using a string from its metadata', function (assert) { - const Tag = DS.Model.extend({}); + const Tag = Model.extend({}); - const Person = DS.Model.extend({ - tags: DS.hasMany('tag', { async: false }), + const Person = Model.extend({ + tags: hasMany('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -969,10 +963,10 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); test('should be able to retrieve the type for a belongsTo relationship without specifying a type from its metadata', function (assert) { - const Tag = DS.Model.extend({}); + const Tag = Model.extend({}); - const Person = DS.Model.extend({ - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + tag: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -988,12 +982,12 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); test('should be able to retrieve the type for a belongsTo relationship specified using a string from its metadata', function (assert) { - const Tag = DS.Model.extend({ - name: DS.attr('string'), + const Tag = Model.extend({ + name: attr('string'), }); - const Person = DS.Model.extend({ - tags: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + tags: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -1011,13 +1005,13 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('relationships work when declared with a string path', function (assert) { assert.expect(2); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: false }), }); - const Tag = DS.Model.extend({ - name: DS.attr('string'), + const Tag = Model.extend({ + name: attr('string'), }); this.owner.register('model:person', Person); @@ -1082,14 +1076,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('hasMany relationships work when the data hash has not been loaded', function (assert) { assert.expect(8); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: true }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: true }), }); this.owner.register('model:tag', Tag); @@ -1151,14 +1145,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('it is possible to add a new item to a relationship', function (assert) { assert.expect(2); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.belongsTo('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + people: belongsTo('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -1212,14 +1206,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('new items added to a hasMany relationship are not cleared by a delete', function (assert) { assert.expect(4); - const Person = DS.Model.extend({ - name: DS.attr('string'), - pets: DS.hasMany('pet', { async: false, inverse: null }), + const Person = Model.extend({ + name: attr('string'), + pets: hasMany('pet', { async: false, inverse: null }), }); - const Pet = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false, inverse: null }), + const Pet = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false, inverse: null }), }); this.owner.register('model:person', Person); @@ -1311,14 +1305,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { todo('[push hasMany] new items added to a hasMany relationship are not cleared by a store.push', function (assert) { assert.expect(5); - const Person = DS.Model.extend({ - name: DS.attr('string'), - pets: DS.hasMany('pet', { async: false, inverse: null }), + const Person = Model.extend({ + name: attr('string'), + pets: hasMany('pet', { async: false, inverse: null }), }); - const Pet = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false, inverse: null }), + const Pet = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false, inverse: null }), }); this.owner.register('model:person', Person); @@ -1426,14 +1420,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { todo('[push hasMany] items removed from a hasMany relationship are not cleared by a store.push', function (assert) { assert.expect(5); - const Person = DS.Model.extend({ - name: DS.attr('string'), - pets: DS.hasMany('pet', { async: false, inverse: null }), + const Person = Model.extend({ + name: attr('string'), + pets: hasMany('pet', { async: false, inverse: null }), }); - const Pet = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false, inverse: null }), + const Pet = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false, inverse: null }), }); this.owner.register('model:person', Person); @@ -1547,14 +1541,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('new items added to an async hasMany relationship are not cleared by a delete', function (assert) { assert.expect(7); - const Person = DS.Model.extend({ - name: DS.attr('string'), - pets: DS.hasMany('pet', { async: true, inverse: null }), + const Person = Model.extend({ + name: attr('string'), + pets: hasMany('pet', { async: true, inverse: null }), }); - const Pet = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false, inverse: null }), + const Pet = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false, inverse: null }), }); this.owner.register('model:person', Person); @@ -1649,13 +1643,13 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('new items added to a belongsTo relationship are not cleared by a delete', function (assert) { assert.expect(4); - const Person = DS.Model.extend({ - name: DS.attr('string'), - dog: DS.belongsTo('dog', { async: false, inverse: null }), + const Person = Model.extend({ + name: attr('string'), + dog: belongsTo('dog', { async: false, inverse: null }), }); - const Dog = DS.Model.extend({ - name: DS.attr('string'), + const Dog = Model.extend({ + name: attr('string'), }); this.owner.register('model:person', Person); @@ -1728,13 +1722,13 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('new items added to an async belongsTo relationship are not cleared by a delete', function (assert) { assert.expect(4); - const Person = DS.Model.extend({ - name: DS.attr('string'), - dog: DS.belongsTo('dog', { async: true, inverse: null }), + const Person = Model.extend({ + name: attr('string'), + dog: belongsTo('dog', { async: true, inverse: null }), }); - const Dog = DS.Model.extend({ - name: DS.attr('string'), + const Dog = Model.extend({ + name: attr('string'), }); this.owner.register('model:person', Person); @@ -1807,13 +1801,13 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('deleting an item that is the current state of a belongsTo clears currentState', function (assert) { assert.expect(4); - const Person = DS.Model.extend({ - name: DS.attr('string'), - dog: DS.belongsTo('dog', { async: false, inverse: null }), + const Person = Model.extend({ + name: attr('string'), + dog: belongsTo('dog', { async: false, inverse: null }), }); - const Dog = DS.Model.extend({ - name: DS.attr('string'), + const Dog = Model.extend({ + name: attr('string'), }); this.owner.register('model:person', Person); @@ -1884,26 +1878,22 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); test('hasMany.firstObject.unloadRecord should not break that hasMany', function (assert) { - const Person = DS.Model.extend({ - cars: DS.hasMany('car', { async: false }), - name: DS.attr(), + const Person = Model.extend({ + cars: hasMany('car', { async: false }), + name: attr(), }); - Person.reopenClass({ - toString() { - return 'person'; - }, - }); + Person.toString = () => { + return 'person'; + }; - const Car = DS.Model.extend({ - name: DS.attr(), + const Car = Model.extend({ + name: attr(), }); - Car.reopenClass({ - toString() { - return 'car'; - }, - }); + Car.toString = () => { + return 'car'; + }; this.owner.register('model:person', Person); this.owner.register('model:car', Car); @@ -1961,14 +1951,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('[ASSERTS KNOWN LIMITATION STILL EXISTS] returning new hasMany relationship info from a delete clears local state', function (assert) { assert.expect(4); - const Person = DS.Model.extend({ - name: DS.attr('string'), - pets: DS.hasMany('pet', { async: false, inverse: null }), + const Person = Model.extend({ + name: attr('string'), + pets: hasMany('pet', { async: false, inverse: null }), }); - const Pet = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false, inverse: null }), + const Pet = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false, inverse: null }), }); this.owner.register('model:person', Person); @@ -2079,14 +2069,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('possible to replace items in a relationship using setObjects w/ Ember Enumerable Array/Object as the argument (GH-2533)', function (assert) { assert.expect(2); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -2144,7 +2134,7 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { run(() => { tom = store.peekRecord('person', '1'); sylvain = store.peekRecord('person', '2'); - // Test that since sylvain.get('tags') instanceof DS.ManyArray, + // Test that since sylvain.get('tags') instanceof ManyArray, // addInternalModels on Relationship iterates correctly. tom.get('tags').setObjects(sylvain.get('tags')); }); @@ -2156,14 +2146,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('Replacing `has-many` with non-array will throw assertion', function (assert) { assert.expect(1); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -2217,14 +2207,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('it is possible to remove an item from a relationship', function (assert) { assert.expect(2); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -2275,14 +2265,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); test('it is possible to add an item to a relationship, remove it, then add it again', function (assert) { - const Tag = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Tag = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: false }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -2316,14 +2306,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); test('hasMany is async by default', function (assert) { - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person'), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person'), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -2332,18 +2322,18 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { let store = this.owner.lookup('service:store'); let tag = store.createRecord('tag'); - assert.ok(tag.get('people') instanceof DS.PromiseManyArray, 'people should be an async relationship'); + assert.ok(tag.get('people') instanceof PromiseManyArray, 'people should be an async relationship'); }); test('hasMany is stable', function (assert) { - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person'), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person'), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -2365,14 +2355,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); test('hasMany proxy is destroyed', function (assert) { - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person'), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person'), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -2629,19 +2619,19 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { assert.true(firstPostCommentsPromise.isFulfilled, 'comments relationship is fulfilled'); }); - test('DS.ManyArray is lazy', async function (assert) { + test('ManyArray is lazy', async function (assert) { let peopleDidChange = 0; - const Tag = DS.Model.extend({ - name: DS.attr('string'), - people: DS.hasMany('person'), + const Tag = Model.extend({ + name: attr('string'), + people: hasMany('person'), peopleDidChange: observer('people.@each', function () { peopleDidChange++; }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tag: DS.belongsTo('tag', { async: false }), + const Person = Model.extend({ + name: attr('string'), + tag: belongsTo('tag', { async: false }), }); this.owner.register('model:tag', Tag); @@ -2665,7 +2655,7 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { 0, 'expect people hasMany to not emit a change event (after access, but after the current run loop)' ); - //assert.ok(hasManyRelationship._manyArray instanceof DS.ManyArray); + //assert.ok(hasManyRelationship._manyArray instanceof ManyArray); let person = store.createRecord('person'); @@ -2678,14 +2668,14 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { test('fetch hasMany loads full relationship after a parent and child have been loaded', function (assert) { assert.expect(4); - const Tag = DS.Model.extend({ - name: DS.attr('string'), - person: DS.belongsTo('person', { async: true, inverse: 'tags' }), + const Tag = Model.extend({ + name: attr('string'), + person: belongsTo('person', { async: true, inverse: 'tags' }), }); - const Person = DS.Model.extend({ - name: DS.attr('string'), - tags: DS.hasMany('tag', { async: true, inverse: 'person' }), + const Person = Model.extend({ + name: attr('string'), + tags: hasMany('tag', { async: true, inverse: 'person' }), }); this.owner.register('model:tag', Tag); @@ -2755,9 +2745,9 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); testInDebug('throws assertion if of not set with an array', function (assert) { - const Person = DS.Model.extend(); - const Tag = DS.Model.extend({ - people: DS.hasMany('person'), + const Person = Model.extend(); + const Tag = Model.extend({ + people: hasMany('person'), }); this.owner.register('model:tag', Tag); @@ -2774,10 +2764,10 @@ module('unit/model/relationships - DS.hasMany', function (hooks) { }); }); - testInDebug('checks if passed array only contains instances of DS.Model', function (assert) { - const Person = DS.Model.extend(); - const Tag = DS.Model.extend({ - people: DS.hasMany('person'), + testInDebug('checks if passed array only contains instances of Model', function (assert) { + const Person = Model.extend(); + const Tag = Model.extend({ + people: hasMany('person'), }); this.owner.register('model:tag', Tag); diff --git a/packages/-ember-data/tests/unit/model/rollback-attributes-test.js b/packages/-ember-data/tests/unit/model/rollback-attributes-test.js index fa57ae2df7c..fbfdbde8d32 100644 --- a/packages/-ember-data/tests/unit/model/rollback-attributes-test.js +++ b/packages/-ember-data/tests/unit/model/rollback-attributes-test.js @@ -30,11 +30,6 @@ module('unit/model/rollbackAttributes - model.rollbackAttributes()', function (h this.incrementProperty('rolledBackCount'); }, }); - Person.reopenClass({ - toString() { - return 'Person'; - }, - }); this.owner.register('model:person', Person); this.owner.register('adapter:application', Adapter.extend()); diff --git a/packages/-ember-data/tests/unit/store/adapter-interop-test.js b/packages/-ember-data/tests/unit/store/adapter-interop-test.js index 4ce9424999d..fa616fbe99b 100644 --- a/packages/-ember-data/tests/unit/store/adapter-interop-test.js +++ b/packages/-ember-data/tests/unit/store/adapter-interop-test.js @@ -387,12 +387,6 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho name: attr('string'), }); - Person.reopenClass({ - toString() { - return 'Person'; - }, - }); - this.owner.register('model:person', Person); let store = this.owner.lookup('service:store'); @@ -1283,5 +1277,7 @@ module('unit/store/adapter-interop - Store working with a Adapter', function (ho assert.expectAssertion(() => { store.adapterFor(Person); }, /Passing classes to store.adapterFor has been removed/); + + assert.expectDeprecation({ id: 'ember-data:deprecate-early-static' }); }); }); diff --git a/packages/-ember-data/tests/unit/store/push-test.js b/packages/-ember-data/tests/unit/store/push-test.js index 1dc708498b6..bac04624a86 100644 --- a/packages/-ember-data/tests/unit/store/push-test.js +++ b/packages/-ember-data/tests/unit/store/push-test.js @@ -4,45 +4,37 @@ import { run } from '@ember/runloop'; import { module, test } from 'qunit'; import { resolve } from 'rsvp'; -import DS from 'ember-data'; import { setupTest } from 'ember-qunit'; +import Adapter from '@ember-data/adapter'; +import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; +import JSONAPISerializer from '@ember-data/serializer/json-api'; +import RESTSerializer from '@ember-data/serializer/rest'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; -let store, Person, PhoneNumber, Post; -const { attr, hasMany, belongsTo } = DS; - -module('unit/store/push - DS.Store#push', function (hooks) { +module('unit/store/push - Store#push', function (hooks) { setupTest(hooks); hooks.beforeEach(function () { - Person = DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - phoneNumbers: hasMany('phone-number', { async: false }), - friends: hasMany('person', { async: false, inverse: 'friends' }), // many to many - }); - - PhoneNumber = DS.Model.extend({ - number: attr('string'), - person: belongsTo('person', { async: false }), - }); - - Post = DS.Model.extend({ - postTitle: attr('string'), - }); - + class Person extends Model { + @attr firstName; + @attr lastName; + @hasMany('phone-number', { async: false, inverse: 'person' }) phoneNumbers; + @hasMany('person', { async: false, inverse: 'friends' }) friends; // many to many + } this.owner.register('model:person', Person); - this.owner.register('model:phone-number', PhoneNumber); - this.owner.register('model:post', Post); - store = this.owner.lookup('service:store'); + class PhoneNumber extends Model { + @attr number; + @belongsTo('person', { async: false, inverse: 'phoneNumbers' }) person; + } + this.owner.register('model:phone-number', PhoneNumber); - this.owner.register('serializer:application', DS.JSONAPISerializer.extend()); - this.owner.register('serializer:post', DS.RESTSerializer.extend()); + this.owner.register('serializer:application', JSONAPISerializer.extend()); }); test('Changed attributes are reset when matching data is pushed', function (assert) { + const store = this.owner.lookup('service:store'); let person = store.push({ data: { type: 'person', @@ -89,6 +81,7 @@ module('unit/store/push - DS.Store#push', function (hooks) { test('Calling push with a normalized hash returns a record', function (assert) { assert.expect(2); + const store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -121,40 +114,9 @@ module('unit/store/push - DS.Store#push', function (hooks) { }); }); - test('Supplying a model class for `push` is the same as supplying a string', function (assert) { - assert.expect(1); - - let adapter = store.adapterFor('application'); - - adapter.shouldBackgroundReloadRecord = () => false; - - const Programmer = Person.extend(); - this.owner.register('model:programmer', Programmer); - - return run(() => { - store.push({ - data: { - type: 'programmer', - id: 'wat', - attributes: { - firstName: 'Yehuda', - lastName: 'Katz', - }, - }, - }); - - return store.findRecord('programmer', 'wat').then((foundProgrammer) => { - assert.deepEqual(foundProgrammer.getProperties('id', 'firstName', 'lastName'), { - id: 'wat', - firstName: 'Yehuda', - lastName: 'Katz', - }); - }); - }); - }); - test('Calling push with partial records updates just those attributes', function (assert) { assert.expect(2); + const store = this.owner.lookup('service:store'); let adapter = store.adapterFor('application'); @@ -200,8 +162,9 @@ module('unit/store/push - DS.Store#push', function (hooks) { }); test('Calling push on normalize allows partial updates with raw JSON', function (assert) { - this.owner.register('serializer:person', DS.RESTSerializer); + this.owner.register('serializer:person', RESTSerializer); let person; + const store = this.owner.lookup('service:store'); run(() => { person = store.push({ @@ -229,12 +192,15 @@ module('unit/store/push - DS.Store#push', function (hooks) { test('Calling push with a normalized hash containing IDs of related records returns a record', function (assert) { assert.expect(1); + const store = this.owner.lookup('service:store'); - Person.reopen({ - phoneNumbers: hasMany('phone-number', { - async: true, - }), - }); + class Person extends Model { + @attr firstName; + @attr lastName; + @hasMany('phone-number', { async: true, inverse: 'person' }) phoneNumbers; + @hasMany('person', { async: false, inverse: 'friends' }) friends; // many to many + } + this.owner.register('model:person', Person); let adapter = store.adapterFor('application'); @@ -309,632 +275,687 @@ module('unit/store/push - DS.Store#push', function (hooks) { }); }); - test('Calling pushPayload allows pushing raw JSON', function (assert) { - run(() => { - store.pushPayload('post', { - posts: [ - { - id: '1', - postTitle: 'Ember rocks', - }, - ], - }); - }); + testInDebug('calling push without data argument as an object raises an error', function (assert) { + const store = this.owner.lookup('service:store'); + let invalidValues = [null, 1, 'string', EmberObject.create(), EmberObject.extend(), true]; - let post = store.peekRecord('post', 1); + assert.expect(invalidValues.length); - assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + invalidValues.forEach((invalidValue) => { + assert.expectAssertion(() => { + run(() => { + store.push('person', invalidValue); + }); + }, /object/); + }); + }); - run(() => { - store.pushPayload('post', { - posts: [ - { + testInDebug('Calling push with a link for a non async relationship should warn if no data', function (assert) { + const store = this.owner.lookup('service:store'); + assert.expectWarning(() => { + run(() => { + store.push({ + data: { + type: 'person', id: '1', - postTitle: 'Ember rocks (updated)', + relationships: { + phoneNumbers: { + links: { + related: '/api/people/1/phone-numbers', + }, + }, + }, }, - ], + }); }); - }); - - assert.strictEqual(post.get('postTitle'), 'Ember rocks (updated)', 'You can update data in the store'); + }, /You pushed a record of type 'person' with a relationship 'phoneNumbers' configured as 'async: false'. You've included a link but no primary data, this may be an error in your payload. EmberData will treat this relationship as known-to-be-empty./); }); - test('Calling pushPayload allows pushing singular payload properties', function (assert) { - run(() => { - store.pushPayload('post', { - post: { + testInDebug( + 'Calling push with a link for a non async relationship should not warn when data is present', + function (assert) { + const store = this.owner.lookup('service:store'); + assert.expectNoWarning(() => { + run(() => { + store.push({ + data: { + type: 'person', + id: '1', + relationships: { + phoneNumbers: { + data: [ + { type: 'phone-number', id: '2' }, + { type: 'phone-number', id: '3' }, + ], + links: { + related: '/api/people/1/phone-numbers', + }, + }, + }, + }, + }); + }); + }); + } + ); + + testInDebug( + 'Calling push with a link for a non async relationship should not reset an existing relationship', + function (assert) { + const store = this.owner.lookup('service:store'); + // GET /persons/1?include=phone-numbers + store.push({ + data: { + type: 'person', id: '1', - postTitle: 'Ember rocks', + relationships: { + phoneNumbers: { + data: [{ type: 'phone-number', id: '2' }], + links: { + related: '/api/people/1/phone-numbers', + }, + }, + }, }, + included: [ + { + type: 'phone-number', + id: '2', + attributes: { + number: '1-800-DATA', + }, + }, + ], }); - }); - let post = store.peekRecord('post', 1); + let person = store.peekRecord('person', 1); - assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + assert.strictEqual(person.get('phoneNumbers.length'), 1); + assert.strictEqual(person.get('phoneNumbers.firstObject.number'), '1-800-DATA'); - run(() => { - store.pushPayload('post', { - post: { - id: '1', - postTitle: 'Ember rocks (updated)', - }, + // GET /persons/1 + assert.expectNoWarning(() => { + store.push({ + data: { + type: 'person', + id: '1', + relationships: { + phoneNumbers: { + links: { + related: '/api/people/1/phone-numbers', + }, + }, + }, + }, + }); }); - }); - - assert.strictEqual(post.get('postTitle'), 'Ember rocks (updated)', 'You can update data in the store'); - }); - test(`Calling pushPayload should use the type's serializer for normalizing`, function (assert) { - assert.expect(4); + assert.strictEqual(person.get('phoneNumbers.length'), 1); + assert.strictEqual(person.get('phoneNumbers.firstObject.number'), '1-800-DATA'); + } + ); - this.owner.register( - 'serializer:post', - DS.RESTSerializer.extend({ - normalize() { - assert.ok(true, 'normalized is called on Post serializer'); - return this._super(...arguments); - }, - }) - ); + testInDebug('Calling push with an unknown model name throws an assertion error', function (assert) { + const store = this.owner.lookup('service:store'); + assert.expectAssertion(() => { + run(() => { + store.push({ + data: { + id: '1', + type: 'unknown', + }, + }); + }); + }, /You tried to push data with a type 'unknown' but no model could be found with that name/); + }); - this.owner.register( - 'serializer:person', - DS.RESTSerializer.extend({ - normalize() { - assert.ok(true, 'normalized is called on Person serializer'); - return this._super(...arguments); - }, - }) - ); + test('Calling push with a link containing an object', function (assert) { + class Person extends Model { + @attr firstName; + @attr lastName; + @hasMany('phone-number', { async: true, inverse: 'person' }) phoneNumbers; + @hasMany('person', { async: false, inverse: 'friends' }) friends; // many to many + } + this.owner.register('model:person', Person); + const store = this.owner.lookup('service:store'); run(() => { - store.pushPayload('post', { - posts: [ - { - id: 1, - postTitle: 'Ember rocks', + store.push( + store.normalize('person', { + id: '1', + type: 'person', + attributes: { + 'first-name': 'Tan', }, - ], - people: [ - { - id: 2, - firstName: 'Yehuda', + relationships: { + 'phone-numbers': { + links: { related: '/api/people/1/phone-numbers' }, + }, }, - ], - }); + }) + ); }); - let post = store.peekRecord('post', 1); - - assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); - - let person = store.peekRecord('person', 2); + let person = store.peekRecord('person', 1); - assert.strictEqual(person.get('firstName'), 'Yehuda', 'you can push raw JSON into the store'); + assert.strictEqual(person.get('firstName'), 'Tan', 'you can use links containing an object'); }); - test(`Calling pushPayload without a type uses application serializer's pushPayload method`, function (assert) { - assert.expect(1); - - this.owner.register( - 'serializer:application', - DS.RESTSerializer.extend({ - pushPayload() { - assert.ok(true, `pushPayload is called on Application serializer`); - return this._super(...arguments); - }, - }) - ); - + test('Calling push with a link containing the value null', function (assert) { + const store = this.owner.lookup('service:store'); run(() => { - store.pushPayload({ - posts: [{ id: '1', postTitle: 'Ember rocks' }], + store.push( + store.normalize('person', { + id: '1', + type: 'person', + attributes: { + 'first-name': 'Tan', + }, + relationships: { + 'phone-numbers': { + links: { + related: null, + }, + }, + }, + }) + ); + }); + + let person = store.peekRecord('person', 1); + + assert.strictEqual(person.get('firstName'), 'Tan', 'you can use links that contain null as a value'); + }); + + testInDebug('calling push with hasMany relationship the value must be an array', function (assert) { + const store = this.owner.lookup('service:store'); + assert.expectAssertion(() => { + run(() => { + store.push({ + data: { + type: 'person', + id: '1', + relationships: { + phoneNumbers: { + data: 1, + }, + }, + }, + }); }); }); }); - test(`Calling pushPayload without a type should use a model's serializer when normalizing`, function (assert) { - assert.expect(4); + testInDebug('calling push with missing or invalid `id` throws assertion error', function (assert) { + const store = this.owner.lookup('service:store'); + let invalidValues = [{}, { id: null }, { id: '' }]; - this.owner.register( - 'serializer:post', - DS.RESTSerializer.extend({ - normalize() { - assert.ok(true, 'normalized is called on Post serializer'); - return this._super(...arguments); - }, - }) - ); + assert.expect(invalidValues.length); - this.owner.register( - 'serializer:application', - DS.RESTSerializer.extend({ - normalize() { - assert.ok(true, 'normalized is called on Application serializer'); - return this._super(...arguments); - }, - }) - ); + invalidValues.forEach((invalidValue) => { + assert.expectAssertion(() => { + run(() => { + store.push({ + data: invalidValue, + }); + }); + }, /You must include an 'id'/); + }); + }); - run(() => { - store.pushPayload({ - posts: [ - { + testInDebug('calling push with belongsTo relationship the value must not be an array', function (assert) { + const store = this.owner.lookup('service:store'); + assert.expectAssertion(() => { + run(() => { + store.push({ + data: { + type: 'phone-number', id: '1', - postTitle: 'Ember rocks', + relationships: { + person: { + data: [1], + }, + }, }, - ], - people: [ - { - id: '2', - firstName: 'Yehuda', + }); + }); + }, /must not be an array/); + }); + + testInDebug('Calling push with unknown keys should not warn by default', function (assert) { + const store = this.owner.lookup('service:store'); + assert.expectNoWarning(() => { + run(() => { + store.push({ + data: { + type: 'person', + id: '1', + attributes: { + firstName: 'Tomster', + emailAddress: 'tomster@emberjs.com', + isMascot: true, + }, }, - ], + }); }); + }, /The payload for 'person' contains these unknown .*: .* Make sure they've been defined in your model./); + }); + + test('_push returns an identifier if an object is pushed', function (assert) { + const store = this.owner.lookup('service:store'); + let pushResult = store._push({ + data: { + id: 1, + type: 'person', + }, }); - var post = store.peekRecord('post', 1); + assert.strictEqual(pushResult, store.identifierCache.getOrCreateRecordIdentifier({ type: 'person', id: '1' })); + assert.notOk(store._instanceCache.peek(pushResult, { bucket: 'record' }), 'record is not materialized'); + }); - assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + test('_push does not require a modelName to resolve to a modelClass', function (assert) { + const store = this.owner.lookup('service:store'); + let originalCall = store.modelFor; + store.modelFor = function () { + assert.notOk('modelFor was triggered as a result of a call to store._push'); + }; - var person = store.peekRecord('person', 2); + run(() => { + store._push({ + data: { + id: 1, + type: 'person', + }, + }); + }); - assert.strictEqual(person.get('firstName'), 'Yehuda', 'you can push raw JSON into the store'); + store.modelFor = originalCall; + assert.ok('We made it'); }); - test('Calling pushPayload allows partial updates with raw JSON', function (assert) { - this.owner.register('serializer:person', DS.RESTSerializer); + test('_push returns an array of identifiers if an array is pushed', function (assert) { + const store = this.owner.lookup('service:store'); + let pushResult; run(() => { - store.pushPayload('person', { - people: [ + pushResult = store._push({ + data: [ { - id: '1', - firstName: 'Robert', - lastName: 'Jackson', + id: 1, + type: 'person', }, ], }); }); - let person = store.peekRecord('person', 1); + assert.ok(pushResult instanceof Array); + assert.strictEqual(pushResult[0], store.identifierCache.getOrCreateRecordIdentifier({ type: 'person', id: '1' })); + assert.notOk(store._instanceCache.peek(pushResult[0], { bucket: 'record' }), 'record is not materialized'); + }); - assert.strictEqual(person.get('firstName'), 'Robert', 'you can push raw JSON into the store'); - assert.strictEqual(person.get('lastName'), 'Jackson', 'you can push raw JSON into the store'); + test('_push returns null if no data is pushed', function (assert) { + const store = this.owner.lookup('service:store'); + let pushResult; run(() => { - store.pushPayload('person', { - people: [ - { - id: '1', - firstName: 'Jacquie', - }, - ], + pushResult = store._push({ + data: null, }); }); - assert.strictEqual(person.get('firstName'), 'Jacquie', 'you can push raw JSON into the store'); - assert.strictEqual(person.get('lastName'), 'Jackson', 'existing fields are untouched'); + assert.strictEqual(pushResult, null); }); +}); - testInDebug( - 'Calling pushPayload with a record does not reorder the hasMany it is in when a many-many relationship', - function (assert) { - // one person with two friends - // if we push a change to a friend, the - // person's friends should be in the same order - // at the end +module('unit/store/push - Store#pushPayload', function (hooks) { + setupTest(hooks); + hooks.beforeEach(function () { + class Post extends Model { + @attr postTitle; + } + this.owner.register('model:post', Post); + this.owner.register('serializer:post', RESTSerializer.extend()); + }); - store.pushPayload({ - data: [ + test('Calling pushPayload allows pushing raw JSON', function (assert) { + const store = this.owner.lookup('service:store'); + run(() => { + store.pushPayload('post', { + posts: [ { id: '1', - type: 'person', - attributes: { - 'first-name': 'Robert', - 'last-name': 'Jackson', - }, - relationships: { - friends: { - data: [ - { id: '2', type: 'person' }, - { id: '3', type: 'person' }, - ], - }, - }, - }, - ], - included: [ - { - id: '2', - type: 'person', - attributes: { - 'first-name': 'Friend', - 'last-name': 'One', - }, - }, - { - id: '3', - type: 'person', - attributes: { - 'first-name': 'Friend', - 'last-name': 'Two', - }, + postTitle: 'Ember rocks', }, ], }); + }); - store.pushPayload({ - data: [ + let post = store.peekRecord('post', 1); + + assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + + run(() => { + store.pushPayload('post', { + posts: [ { - id: '2', - type: 'person', - relationships: { - friends: { - data: [{ id: '1', type: 'person' }], - }, - }, + id: '1', + postTitle: 'Ember rocks (updated)', }, ], }); + }); - let robert = store.peekRecord('person', '1'); + assert.strictEqual(post.get('postTitle'), 'Ember rocks (updated)', 'You can update data in the store'); + }); - var friends = robert.friends; - assert.strictEqual(friends.firstObject.id, '2', 'first object is unchanged'); - assert.strictEqual(friends.lastObject.id, '3', 'last object is unchanged'); - } - ); + test('Calling pushPayload allows pushing singular payload properties', function (assert) { + const store = this.owner.lookup('service:store'); - testInDebug('calling push without data argument as an object raises an error', function (assert) { - let invalidValues = [null, 1, 'string', EmberObject.create(), EmberObject.extend(), true]; + run(() => { + store.pushPayload('post', { + post: { + id: '1', + postTitle: 'Ember rocks', + }, + }); + }); - assert.expect(invalidValues.length); + let post = store.peekRecord('post', 1); - invalidValues.forEach((invalidValue) => { - assert.expectAssertion(() => { - run(() => { - store.push('person', invalidValue); - }); - }, /object/); - }); - }); + assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); - testInDebug('Calling push with a link for a non async relationship should warn if no data', function (assert) { - Person.reopen({ - phoneNumbers: hasMany('phone-number', { async: false }), + run(() => { + store.pushPayload('post', { + post: { + id: '1', + postTitle: 'Ember rocks (updated)', + }, + }); }); - assert.expectWarning(() => { - run(() => { - store.push({ - data: { - type: 'person', - id: '1', - relationships: { - phoneNumbers: { - links: { - related: '/api/people/1/phone-numbers', - }, - }, - }, - }, - }); - }); - }, /You pushed a record of type 'person' with a relationship 'phoneNumbers' configured as 'async: false'. You've included a link but no primary data, this may be an error in your payload. EmberData will treat this relationship as known-to-be-empty./); + assert.strictEqual(post.get('postTitle'), 'Ember rocks (updated)', 'You can update data in the store'); }); - testInDebug( - 'Calling push with a link for a non async relationship should not warn when data is present', - function (assert) { - Person.reopen({ - phoneNumbers: hasMany('phone-number', { async: false }), - }); + test(`Calling pushPayload should use the type's serializer for normalizing`, function (assert) { + assert.expect(4); + this.owner.register( + 'model:person', + class extends Model { + @attr firstName; + } + ); + this.owner.register( + 'serializer:post', + RESTSerializer.extend({ + normalize() { + assert.ok(true, 'normalized is called on Post serializer'); + return this._super(...arguments); + }, + }) + ); - assert.expectNoWarning(() => { - run(() => { - store.push({ - data: { - type: 'person', - id: '1', - relationships: { - phoneNumbers: { - data: [ - { type: 'phone-number', id: '2' }, - { type: 'phone-number', id: '3' }, - ], - links: { - related: '/api/people/1/phone-numbers', - }, - }, - }, - }, - }); - }); - }); - } - ); + this.owner.register( + 'serializer:person', + RESTSerializer.extend({ + normalize() { + assert.ok(true, 'normalized is called on Person serializer'); + return this._super(...arguments); + }, + }) + ); - testInDebug( - 'Calling push with a link for a non async relationship should not reset an existing relationship', - function (assert) { - // GET /persons/1?include=phone-numbers - store.push({ - data: { - type: 'person', - id: '1', - relationships: { - phoneNumbers: { - data: [{ type: 'phone-number', id: '2' }], - links: { - related: '/api/people/1/phone-numbers', - }, - }, + const store = this.owner.lookup('service:store'); + + run(() => { + store.pushPayload('post', { + posts: [ + { + id: 1, + postTitle: 'Ember rocks', }, - }, - included: [ + ], + people: [ { - type: 'phone-number', - id: '2', - attributes: { - number: '1-800-DATA', - }, + id: 2, + firstName: 'Yehuda', }, ], }); + }); - let person = store.peekRecord('person', 1); + let post = store.peekRecord('post', '1'); - assert.strictEqual(person.get('phoneNumbers.length'), 1); - assert.strictEqual(person.get('phoneNumbers.firstObject.number'), '1-800-DATA'); + assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); - // GET /persons/1 - assert.expectNoWarning(() => { - store.push({ - data: { - type: 'person', - id: '1', - relationships: { - phoneNumbers: { - links: { - related: '/api/people/1/phone-numbers', - }, - }, - }, - }, - }); - }); + let person = store.peekRecord('person', '2'); - assert.strictEqual(person.get('phoneNumbers.length'), 1); - assert.strictEqual(person.get('phoneNumbers.firstObject.number'), '1-800-DATA'); - } - ); + assert.strictEqual(person.get('firstName'), 'Yehuda', 'you can push raw JSON into the store'); + }); - testInDebug('Calling push with an unknown model name throws an assertion error', function (assert) { - assert.expectAssertion(() => { - run(() => { - store.push({ - data: { - id: '1', - type: 'unknown', - }, - }); + test(`Calling pushPayload without a type uses application serializer's pushPayload method`, function (assert) { + assert.expect(1); + + this.owner.register( + 'serializer:application', + RESTSerializer.extend({ + pushPayload() { + assert.ok(true, `pushPayload is called on Application serializer`); + return this._super(...arguments); + }, + }) + ); + const store = this.owner.lookup('service:store'); + + run(() => { + store.pushPayload({ + posts: [{ id: '1', postTitle: 'Ember rocks' }], }); - }, /You tried to push data with a type 'unknown' but no model could be found with that name/); + }); }); - test('Calling push with a link containing an object', function (assert) { - Person.reopen({ - phoneNumbers: hasMany('phone-number', { async: true }), - }); + test(`Calling pushPayload without a type should use a model's serializer when normalizing`, function (assert) { + assert.expect(4); + this.owner.register( + 'model:person', + class extends Model { + @attr firstName; + } + ); + + this.owner.register( + 'serializer:post', + RESTSerializer.extend({ + normalize() { + assert.ok(true, 'normalized is called on Post serializer'); + return this._super(...arguments); + }, + }) + ); + + this.owner.register( + 'serializer:application', + RESTSerializer.extend({ + normalize() { + assert.ok(true, 'normalized is called on Application serializer'); + return this._super(...arguments); + }, + }) + ); + const store = this.owner.lookup('service:store'); run(() => { - store.push( - store.normalize('person', { - id: '1', - type: 'person', - attributes: { - 'first-name': 'Tan', + store.pushPayload({ + posts: [ + { + id: '1', + postTitle: 'Ember rocks', }, - relationships: { - 'phone-numbers': { - links: { related: '/api/people/1/phone-numbers' }, - }, + ], + people: [ + { + id: '2', + firstName: 'Yehuda', }, - }) - ); + ], + }); }); - let person = store.peekRecord('person', 1); + var post = store.peekRecord('post', 1); - assert.strictEqual(person.get('firstName'), 'Tan', 'you can use links containing an object'); + assert.strictEqual(post.get('postTitle'), 'Ember rocks', 'you can push raw JSON into the store'); + + var person = store.peekRecord('person', 2); + + assert.strictEqual(person.get('firstName'), 'Yehuda', 'you can push raw JSON into the store'); }); - test('Calling push with a link containing the value null', function (assert) { + test('Calling pushPayload allows partial updates with raw JSON', function (assert) { + this.owner.register('serializer:person', RESTSerializer); + this.owner.register( + 'model:person', + class extends Model { + @attr firstName; + @attr lastName; + } + ); + + const store = this.owner.lookup('service:store'); run(() => { - store.push( - store.normalize('person', { - id: '1', - type: 'person', - attributes: { - 'first-name': 'Tan', - }, - relationships: { - 'phone-numbers': { - links: { - related: null, - }, - }, + store.pushPayload('person', { + people: [ + { + id: '1', + firstName: 'Robert', + lastName: 'Jackson', }, - }) - ); + ], + }); }); let person = store.peekRecord('person', 1); - assert.strictEqual(person.get('firstName'), 'Tan', 'you can use links that contain null as a value'); - }); + assert.strictEqual(person.get('firstName'), 'Robert', 'you can push raw JSON into the store'); + assert.strictEqual(person.get('lastName'), 'Jackson', 'you can push raw JSON into the store'); - testInDebug('calling push with hasMany relationship the value must be an array', function (assert) { - assert.expectAssertion(() => { - run(() => { - store.push({ - data: { - type: 'person', + run(() => { + store.pushPayload('person', { + people: [ + { id: '1', - relationships: { - phoneNumbers: { - data: 1, - }, - }, + firstName: 'Jacquie', }, - }); + ], }); }); - }); - - testInDebug('calling push with missing or invalid `id` throws assertion error', function (assert) { - let invalidValues = [{}, { id: null }, { id: '' }]; - - assert.expect(invalidValues.length); - invalidValues.forEach((invalidValue) => { - assert.expectAssertion(() => { - run(() => { - store.push({ - data: invalidValue, - }); - }); - }, /You must include an 'id'/); - }); + assert.strictEqual(person.get('firstName'), 'Jacquie', 'you can push raw JSON into the store'); + assert.strictEqual(person.get('lastName'), 'Jackson', 'existing fields are untouched'); }); - testInDebug('calling push with belongsTo relationship the value must not be an array', function (assert) { - assert.expectAssertion(() => { - run(() => { - store.push({ - data: { - type: 'phone-number', + testInDebug( + 'Calling pushPayload with a record does not reorder the hasMany it is in when a many-many relationship', + function (assert) { + class Person extends Model { + @attr firstName; + @attr lastName; + @hasMany('person', { async: false, inverse: 'friends' }) friends; // many to many + } + this.owner.register('model:person', Person); + this.owner.register('serializer:application', JSONAPISerializer.extend()); + // one person with two friends + // if we push a change to a friend, the + // person's friends should be in the same order + // at the end + const store = this.owner.lookup('service:store'); + + store.pushPayload({ + data: [ + { id: '1', + type: 'person', + attributes: { + 'first-name': 'Robert', + 'last-name': 'Jackson', + }, relationships: { - person: { - data: [1], + friends: { + data: [ + { id: '2', type: 'person' }, + { id: '3', type: 'person' }, + ], }, }, }, - }); - }); - }, /must not be an array/); - }); - - testInDebug('Calling push with unknown keys should not warn by default', function (assert) { - assert.expectNoWarning(() => { - run(() => { - store.push({ - data: { + ], + included: [ + { + id: '2', type: 'person', - id: '1', attributes: { - firstName: 'Tomster', - emailAddress: 'tomster@emberjs.com', - isMascot: true, + 'first-name': 'Friend', + 'last-name': 'One', }, }, - }); - }); - }, /The payload for 'person' contains these unknown .*: .* Make sure they've been defined in your model./); - }); - - test('_push returns an identifier if an object is pushed', function (assert) { - let pushResult = store._push({ - data: { - id: 1, - type: 'person', - }, - }); - - assert.strictEqual(pushResult, store.identifierCache.getOrCreateRecordIdentifier({ type: 'person', id: '1' })); - assert.notOk(store._instanceCache.peek(pushResult, { bucket: 'record' }), 'record is not materialized'); - }); - - test('_push does not require a modelName to resolve to a modelClass', function (assert) { - let originalCall = store.modelFor; - store.modelFor = function () { - assert.notOk('modelFor was triggered as a result of a call to store._push'); - }; - - run(() => { - store._push({ - data: { - id: 1, - type: 'person', - }, + { + id: '3', + type: 'person', + attributes: { + 'first-name': 'Friend', + 'last-name': 'Two', + }, + }, + ], }); - }); - store.modelFor = originalCall; - assert.ok('We made it'); - }); - - test('_push returns an array of identifiers if an array is pushed', function (assert) { - let pushResult; - - run(() => { - pushResult = store._push({ + store.pushPayload({ data: [ { - id: 1, + id: '2', type: 'person', + relationships: { + friends: { + data: [{ id: '1', type: 'person' }], + }, + }, }, ], }); - }); - - assert.ok(pushResult instanceof Array); - assert.strictEqual(pushResult[0], store.identifierCache.getOrCreateRecordIdentifier({ type: 'person', id: '1' })); - assert.notOk(store._instanceCache.peek(pushResult[0], { bucket: 'record' }), 'record is not materialized'); - }); - - test('_push returns null if no data is pushed', function (assert) { - let pushResult; - run(() => { - pushResult = store._push({ - data: null, - }); - }); + let robert = store.peekRecord('person', '1'); - assert.strictEqual(pushResult, null); - }); + const friends = robert.friends; + assert.strictEqual(friends.firstObject.id, '2', 'first object is unchanged'); + assert.strictEqual(friends.lastObject.id, '3', 'last object is unchanged'); + } + ); }); -module('unit/store/push - DS.Store#push with JSON-API', function (hooks) { +module('unit/store/push - Store#push with JSON-API', function (hooks) { setupTest(hooks); hooks.beforeEach(function () { - const Person = DS.Model.extend({ - name: DS.attr('string'), - cars: DS.hasMany('car', { async: false }), + const Person = Model.extend({ + name: attr('string'), + cars: hasMany('car', { async: false }), }); - const Car = DS.Model.extend({ - make: DS.attr('string'), - model: DS.attr('string'), - person: DS.belongsTo('person', { async: false }), + const Car = Model.extend({ + make: attr('string'), + model: attr('string'), + person: belongsTo('person', { async: false }), }); this.owner.register('model:person', Person); this.owner.register('model:car', Car); - this.owner.register('adapter:application', DS.Adapter.extend()); - this.owner.register('serializer:application', DS.JSONAPISerializer.extend()); - - store = this.owner.lookup('service:store'); + this.owner.register('adapter:application', Adapter.extend()); + this.owner.register('serializer:application', JSONAPISerializer.extend()); }); test('Should support pushing multiple models into the store', function (assert) { assert.expect(2); + const store = this.owner.lookup('service:store'); run(() => { store.push({ @@ -966,6 +987,7 @@ module('unit/store/push - DS.Store#push with JSON-API', function (hooks) { test('Should support pushing included models into the store', function (assert) { assert.expect(2); + const store = this.owner.lookup('service:store'); run(() => { store.push({ diff --git a/packages/-ember-data/tests/unit/store/serializer-for-test.js b/packages/-ember-data/tests/unit/store/serializer-for-test.js index 92b876af32e..4ed34d0b1fd 100644 --- a/packages/-ember-data/tests/unit/store/serializer-for-test.js +++ b/packages/-ember-data/tests/unit/store/serializer-for-test.js @@ -44,5 +44,7 @@ module('unit/store/serializer_for - DS.Store#serializerFor', function (hooks) { assert.expectAssertion(() => { store.serializerFor(Person); }, /Passing classes to store.serializerFor has been removed/); + + assert.expectDeprecation({ id: 'ember-data:deprecate-early-static' }); }); }); diff --git a/packages/-ember-data/tests/unit/store/unload-test.js b/packages/-ember-data/tests/unit/store/unload-test.js index 3317b2d6d13..dc4182d5d75 100644 --- a/packages/-ember-data/tests/unit/store/unload-test.js +++ b/packages/-ember-data/tests/unit/store/unload-test.js @@ -22,12 +22,6 @@ module('unit/store/unload - Store unloading records', function (hooks) { wasFetched: attr('boolean'), }); - Record.reopenClass({ - toString() { - return 'Record'; - }, - }); - this.owner.register('model:record', Record); this.owner.register('serializer:application', JSONAPISerializer); @@ -136,12 +130,6 @@ module('Store - unload record with relationships', function (hooks) { name: attr('string'), }); - Brand.reopenClass({ - toString() { - return 'Brand'; - }, - }); - const Product = Model.extend({ description: attr('string'), brand: belongsTo('brand', { @@ -149,24 +137,12 @@ module('Store - unload record with relationships', function (hooks) { }), }); - Product.reopenClass({ - toString() { - return 'Product'; - }, - }); - const Like = Model.extend({ product: belongsTo('product', { async: false, }), }); - Like.reopenClass({ - toString() { - return 'Like'; - }, - }); - this.owner.register('model:brand', Brand); this.owner.register('model:product', Product); this.owner.register('model:like', Like); diff --git a/packages/-ember-data/tests/unit/utils-test.js b/packages/-ember-data/tests/unit/utils-test.js index ea968c50d81..89080884fca 100644 --- a/packages/-ember-data/tests/unit/utils-test.js +++ b/packages/-ember-data/tests/unit/utils-test.js @@ -3,10 +3,9 @@ import { run } from '@ember/runloop'; import { module, test } from 'qunit'; -import DS from 'ember-data'; import { setupTest } from 'ember-qunit'; -import Model from '@ember-data/model'; +import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import { modelHasAttributeOrRelationshipNamedType } from '@ember-data/serializer/-private'; import { assertPolymorphicType } from '@ember-data/store/-debug'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; @@ -17,12 +16,12 @@ module('unit/utils', function (hooks) { hooks.beforeEach(function () { const Person = Model.extend(); const User = Model.extend({ - messages: DS.hasMany('message', { async: false }), + messages: hasMany('message', { async: false }), }); const Message = Model.extend(); const Post = Message.extend({ - medias: DS.hasMany('medium', { async: false }), + medias: hasMany('medium', { async: false }), }); const Medium = Mixin.create(); @@ -86,21 +85,27 @@ module('unit/utils', function (hooks) { }); test('modelHasAttributeOrRelationshipNamedType', function (assert) { - let ModelWithTypeAttribute = Model.extend({ - type: DS.attr(), - }); - let ModelWithTypeBelongsTo = Model.extend({ - type: DS.belongsTo(), - }); - let ModelWithTypeHasMany = Model.extend({ - type: DS.hasMany(), - }); + class Blank extends Model {} + class ModelWithTypeAttribute extends Model { + @attr type; + } + class ModelWithTypeBelongsTo extends Model { + @belongsTo type; + } + class ModelWithTypeHasMany extends Model { + @hasMany type; + } + this.owner.register('model:blank', Blank); + this.owner.register('model:with-attr', ModelWithTypeAttribute); + this.owner.register('model:with-belongs-to', ModelWithTypeBelongsTo); + this.owner.register('model:with-has-many', ModelWithTypeHasMany); + const store = this.owner.lookup('service:store'); - assert.false(modelHasAttributeOrRelationshipNamedType(Model)); + assert.false(modelHasAttributeOrRelationshipNamedType(store.modelFor('blank'))); - assert.true(modelHasAttributeOrRelationshipNamedType(ModelWithTypeAttribute)); - assert.true(modelHasAttributeOrRelationshipNamedType(ModelWithTypeBelongsTo)); - assert.true(modelHasAttributeOrRelationshipNamedType(ModelWithTypeHasMany)); + assert.true(modelHasAttributeOrRelationshipNamedType(store.modelFor('with-attr'))); + assert.true(modelHasAttributeOrRelationshipNamedType(store.modelFor('with-belongs-to'))); + assert.true(modelHasAttributeOrRelationshipNamedType(store.modelFor('with-has-many'))); }); testInDebug('assertPolymorphicType works for mixins', function (assert) { diff --git a/packages/model/addon/-private/model-for-mixin.ts b/packages/model/addon/-private/model-for-mixin.ts index 682a7b34ba7..b21be165204 100644 --- a/packages/model/addon/-private/model-for-mixin.ts +++ b/packages/model/addon/-private/model-for-mixin.ts @@ -29,10 +29,8 @@ export default function modelForMixin(store: Store, normalizedModelName: string) let mixin = MaybeMixin && MaybeMixin.class; if (mixin) { let ModelForMixin = Model.extend(mixin); - ModelForMixin.reopenClass({ - __isMixin: true, - __mixin: mixin, - }); + ModelForMixin.__isMixin = true; + ModelForMixin.__mixin = mixin; //Cache the class as a model owner.register('model:' + normalizedModelName, ModelForMixin); } diff --git a/packages/model/addon/-private/model.js b/packages/model/addon/-private/model.js index d9bdf0e9a3c..6cc4f1ffe91 100644 --- a/packages/model/addon/-private/model.js +++ b/packages/model/addon/-private/model.js @@ -2,7 +2,7 @@ @module @ember-data/model */ -import { assert, warn } from '@ember/debug'; +import { assert, deprecate, warn } from '@ember/debug'; import EmberError from '@ember/error'; import EmberObject, { get } from '@ember/object'; import { dependentKeyCompat } from '@ember/object/compat'; @@ -16,7 +16,11 @@ import Ember from 'ember'; import { resolve } from 'rsvp'; import { HAS_DEBUG_PACKAGE } from '@ember-data/private-build-infra'; -import { DEPRECATE_SAVE_PROMISE_ACCESS } from '@ember-data/private-build-infra/deprecations'; +import { + DEPRECATE_EARLY_STATIC, + DEPRECATE_MODEL_REOPEN, + DEPRECATE_SAVE_PROMISE_ACCESS, +} from '@ember-data/private-build-infra/deprecations'; import { recordIdentifierFor, storeFor } from '@ember-data/store'; import { coerceId, @@ -1258,12 +1262,46 @@ class Model extends EmberObject { @return {Model} the type of the relationship, or undefined */ static typeForRelationship(name, store) { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let relationship = this.relationshipsByName.get(name); return relationship && store.modelFor(relationship.type); } @computeOnce static get inverseMap() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } return Object.create(null); } @@ -1301,6 +1339,23 @@ class Model extends EmberObject { @return {Object} the inverse relationship, or null */ static inverseFor(name, store) { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let inverseMap = this.inverseMap; if (inverseMap[name]) { return inverseMap[name]; @@ -1313,6 +1368,23 @@ class Model extends EmberObject { //Calculate the inverse, ignoring the cache static _findInverseFor(name, store) { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let inverseType = this.typeForRelationship(name, store); if (!inverseType) { return null; @@ -1457,6 +1529,23 @@ class Model extends EmberObject { @computeOnce static get relationships() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let map = new Map(); let relationshipsByName = this.relationshipsByName; @@ -1511,6 +1600,23 @@ class Model extends EmberObject { */ @computeOnce static get relationshipNames() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let names = { hasMany: [], belongsTo: [], @@ -1561,6 +1667,23 @@ class Model extends EmberObject { */ @computeOnce static get relatedTypes() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let types = []; let rels = this.relationshipsObject; @@ -1620,6 +1743,23 @@ class Model extends EmberObject { */ @computeOnce static get relationshipsByName() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let map = new Map(); let rels = this.relationshipsObject; let relationships = Object.keys(rels); @@ -1636,6 +1776,23 @@ class Model extends EmberObject { @computeOnce static get relationshipsObject() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let relationships = Object.create(null); let modelName = this.modelName; this.eachComputedProperty((name, meta) => { @@ -1693,6 +1850,23 @@ class Model extends EmberObject { */ @computeOnce static get fields() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let map = new Map(); this.eachComputedProperty((name, meta) => { @@ -1718,6 +1892,23 @@ class Model extends EmberObject { @param {any} binding the value to which the callback's `this` should be bound */ static eachRelationship(callback, binding) { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } this.relationshipsByName.forEach((relationship, name) => { callback.call(binding, name, relationship); }); @@ -1736,6 +1927,23 @@ class Model extends EmberObject { @param {any} binding the value to which the callback's `this` should be bound */ static eachRelatedType(callback, binding) { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let relationshipTypes = this.relatedTypes; for (let i = 0; i < relationshipTypes.length; i++) { @@ -1745,6 +1953,23 @@ class Model extends EmberObject { } static determineRelationshipType(knownSide, store) { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let knownKey = knownSide.key; let knownKind = knownSide.kind; let inverse = this.inverseFor(knownKey, store); @@ -1806,6 +2031,23 @@ class Model extends EmberObject { */ @computeOnce static get attributes() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let map = new Map(); this.eachComputedProperty((name, meta) => { @@ -1865,6 +2107,23 @@ class Model extends EmberObject { */ @computeOnce static get transformedAttributes() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } let map = new Map(); this.eachAttribute((key, meta) => { @@ -1921,6 +2180,23 @@ class Model extends EmberObject { @static */ static eachAttribute(callback, binding) { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } this.attributes.forEach((meta, name) => { callback.call(binding, name, meta); }); @@ -1972,6 +2248,23 @@ class Model extends EmberObject { @static */ static eachTransformedAttribute(callback, binding) { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } this.transformedAttributes.forEach((type, name) => { callback.call(binding, name, type); }); @@ -1985,6 +2278,23 @@ class Model extends EmberObject { @static */ static toString() { + if (DEPRECATE_EARLY_STATIC) { + deprecate( + `Accessing schema information on Models without looking up the model via the store is deprecated. Use store.modelFor (or better Snapshots or the store.getSchemaDefinitionService() apis) instead.`, + this.modelName, + { + id: 'ember-data:deprecate-early-static', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + } else { + assert( + `Accessing schema information on Models without looking up the model via the store is disallowed.`, + this.modelName + ); + } return `model:${get(this, 'modelName')}`; } } @@ -2111,6 +2421,35 @@ if (DEBUG) { } }, }); + + if (DEPRECATE_MODEL_REOPEN) { + const originalReopen = Model.reopen; + const originalReopenClass = Model.reopenClass; + + Model.reopen = function deprecatedReopen() { + deprecate(`Model.reopen is deprecated. Use Foo extends Model to extend your class instead.`, false, { + id: 'ember-data:deprecate-model-reopen', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + }); + return originalReopen.call(this, arguments); + }; + + Model.reopenClass = function deprecatedReopenClass() { + deprecate( + `Model.reopenClass is deprecated. Use Foo extends Model to add static methods and properties to your class instead.`, + false, + { + id: 'ember-data:deprecate-model-reopenclass', + for: 'ember-data', + until: '5.0', + since: { available: '4.8', enabled: '4.8' }, + } + ); + return originalReopenClass.call(this, arguments); + }; + } } export default Model; diff --git a/packages/private-build-infra/addon/current-deprecations.ts b/packages/private-build-infra/addon/current-deprecations.ts index 3386164f9ee..058890fb8d5 100644 --- a/packages/private-build-infra/addon/current-deprecations.ts +++ b/packages/private-build-infra/addon/current-deprecations.ts @@ -47,4 +47,7 @@ export default { DEPRECATE_RECORD_WAS_INVALID: '4.5', DEPRECATE_STRING_ARG_SCHEMAS: '4.5', DEPRECATE_JSON_API_FALLBACK: '4.5', + DEPRECATE_MODEL_REOPEN: '4.8', + DEPRECATE_EARLY_STATIC: '4.8', + DEPRECATE_CLASSIC: '4.9', }; diff --git a/packages/private-build-infra/addon/deprecations.ts b/packages/private-build-infra/addon/deprecations.ts index 357e2af2375..4dc46181e40 100644 --- a/packages/private-build-infra/addon/deprecations.ts +++ b/packages/private-build-infra/addon/deprecations.ts @@ -16,3 +16,6 @@ export const DEPRECATE_HAS_RECORD = deprecationState('DEPRECATE_HAS_RECORD'); export const DEPRECATE_RECORD_WAS_INVALID = deprecationState('DEPRECATE_RECORD_WAS_INVALID'); export const DEPRECATE_STRING_ARG_SCHEMAS = deprecationState('DEPRECATE_STRING_ARG_SCHEMAS'); export const DEPRECATE_JSON_API_FALLBACK = deprecationState('DEPRECATE_JSON_API_FALLBACK'); +export const DEPRECATE_MODEL_REOPEN = deprecationState('DEPRECATE_MODEL_REOPEN'); +export const DEPRECATE_EARLY_STATIC = deprecationState('DEPRECATE_EARLY_STATIC'); +export const DEPRECATE_CLASSIC = deprecationState('DEPRECATE_CLASSIC');