diff --git a/addon/-private/system/store.js b/addon/-private/system/store.js index 1dc458d048f..0cb0b2bcb15 100644 --- a/addon/-private/system/store.js +++ b/addon/-private/system/store.js @@ -3065,7 +3065,9 @@ Store = Service.extend({ // no model specific adapter or application adapter, check for an `adapter` // property defined on the store let adapterName = this.get('adapter'); - adapter = _adapterCache[adapterName] || owner.lookup(`adapter:${adapterName}`); + adapter = adapterName + ? _adapterCache[adapterName] || owner.lookup(`adapter:${adapterName}`) + : undefined; if (adapter !== undefined) { set(adapter, 'store', this); _adapterCache[normalizedModelName] = adapter; @@ -3076,6 +3078,10 @@ Store = Service.extend({ // final fallback, no model specific adapter, no application adapter, no // `adapter` property on store: use json-api adapter adapter = _adapterCache['-json-api'] || owner.lookup('adapter:-json-api'); + assert( + `No adapter was found for '${modelName}' and no 'application', store.adapter = 'adapter-fallback-name', or '-json-api' adapter were found as fallbacks.`, + adapter !== undefined + ); set(adapter, 'store', this); _adapterCache[normalizedModelName] = adapter; _adapterCache['-json-api'] = adapter; @@ -3150,7 +3156,9 @@ Store = Service.extend({ // property defined on the adapter let adapter = this.adapterFor(modelName); let serializerName = get(adapter, 'defaultSerializer'); - serializer = _serializerCache[serializerName] || owner.lookup(`serializer:${serializerName}`); + serializer = serializerName + ? _serializerCache[serializerName] || owner.lookup(`serializer:${serializerName}`) + : undefined; if (serializer !== undefined) { set(serializer, 'store', this); _serializerCache[normalizedModelName] = serializer; @@ -3161,6 +3169,10 @@ Store = Service.extend({ // final fallback, no model specific serializer, no application serializer, no // `serializer` property on store: use json-api serializer serializer = _serializerCache['-default'] || owner.lookup('serializer:-default'); + assert( + `No serializer was found for '${modelName}' and no 'application', Adapter.defaultSerializer, or '-default' serializer were found as fallbacks.`, + serializer !== undefined + ); set(serializer, 'store', this); _serializerCache[normalizedModelName] = serializer; _serializerCache['-default'] = serializer; diff --git a/tests/integration/records/load-test.js b/tests/integration/records/load-test.js index 6d36e05e0ac..95aa84775a4 100644 --- a/tests/integration/records/load-test.js +++ b/tests/integration/records/load-test.js @@ -12,6 +12,7 @@ import todo from '../../helpers/todo'; class Person extends Model { @attr name; + @belongsTo('person', { async: true, inverse: 'bestFriend' }) bestFriend; } diff --git a/tests/integration/store/adapter-for-test.js b/tests/integration/store/adapter-for-test.js new file mode 100644 index 00000000000..56b1ee64265 --- /dev/null +++ b/tests/integration/store/adapter-for-test.js @@ -0,0 +1,329 @@ +import { setupTest } from 'ember-qunit'; +import { module, test } from 'qunit'; +import Store from 'ember-data/store'; +import { run } from '@ember/runloop'; + +class TestAdapter { + constructor(args) { + Object.assign(this, args); + this.didInit(); + } + + didInit() {} + + static create(args) { + return new this(args); + } +} + +module('integration/store - adapterFor', function(hooks) { + setupTest(hooks); + let store; + + hooks.beforeEach(function() { + let { owner } = this; + store = owner.lookup('service:store'); + }); + + test('when no adapter is available we throw an error', async function(assert) { + let { owner } = this; + /* + ensure our store instance does not specify a fallback + we use an empty string as that would cause `owner.lookup` to blow up if not guarded properly + whereas `null` `undefined` `false` would not. + */ + store.adapter = ''; + /* + adapter:-json-api is the "last chance" fallback and is + registered automatically. + unregistering it will cause adapterFor to return `undefined`. + */ + owner.unregister('adapter:-json-api'); + + assert.expectAssertion(() => { + store.adapterFor('person'); + }, /No adapter was found for 'person' and no 'application', store\.adapter = 'adapter-fallback-name', or '-json-api' adapter were found as fallbacks\./); + }); + + test('we find and instantiate the application adapter', async function(assert) { + let { owner } = this; + let didInstantiate = false; + + class AppAdapter extends TestAdapter { + didInit() { + didInstantiate = true; + } + } + + owner.register('adapter:application', AppAdapter); + + let adapter = store.adapterFor('application'); + + assert.ok(adapter instanceof AppAdapter, 'We found the correct adapter'); + assert.ok(didInstantiate, 'We instantiated the adapter'); + didInstantiate = false; + + let adapterAgain = store.adapterFor('application'); + + assert.ok(adapterAgain instanceof AppAdapter, 'We found the correct adapter'); + assert.ok(!didInstantiate, 'We did not instantiate the adapter again'); + assert.ok(adapter === adapterAgain, 'Repeated calls to adapterFor return the same instance'); + }); + + test('multiple stores do not share adapters', async function(assert) { + let { owner } = this; + let didInstantiate = false; + + class AppAdapter extends TestAdapter { + didInit() { + didInstantiate = true; + } + } + + owner.register('adapter:application', AppAdapter); + owner.register('service:other-store', Store); + + let otherStore = owner.lookup('service:other-store'); + let adapter = store.adapterFor('application'); + + assert.ok(adapter instanceof AppAdapter, 'We found the correct adapter'); + assert.ok(didInstantiate, 'We instantiated the adapter'); + didInstantiate = false; + + let otherAdapter = otherStore.adapterFor('application'); + assert.ok(otherAdapter instanceof AppAdapter, 'We found the correct adapter again'); + assert.ok(didInstantiate, 'We instantiated the other adapter'); + assert.ok(otherAdapter !== adapter, 'We have a different adapter instance'); + + // Ember 2.18 requires us to wrap destroy in a run. Use `await settled()` for newer versions. + run(() => otherStore.destroy()); + }); + + test('we can find and instantiate per-type adapters', async function(assert) { + let { owner } = this; + let didInstantiateAppAdapter = false; + let didInstantiatePersonAdapter = false; + + class AppAdapter extends TestAdapter { + didInit() { + didInstantiateAppAdapter = true; + } + } + + class PersonAdapter extends TestAdapter { + didInit() { + didInstantiatePersonAdapter = true; + } + } + + owner.register('adapter:application', AppAdapter); + owner.register('adapter:person', PersonAdapter); + + let adapter = store.adapterFor('person'); + + assert.ok(adapter instanceof PersonAdapter, 'We found the correct adapter'); + assert.ok(didInstantiatePersonAdapter, 'We instantiated the person adapter'); + assert.ok(!didInstantiateAppAdapter, 'We did not instantiate the application adapter'); + + let appAdapter = store.adapterFor('application'); + assert.ok(appAdapter instanceof AppAdapter, 'We found the correct adapter'); + assert.ok(didInstantiateAppAdapter, 'We instantiated the application adapter'); + assert.ok(appAdapter !== adapter, 'We have separate adapters'); + }); + + test('we fallback to the application adapter when a per-type adapter is not found', async function(assert) { + let { owner } = this; + let didInstantiateAppAdapter = false; + + class AppAdapter extends TestAdapter { + didInit() { + didInstantiateAppAdapter = true; + } + } + + owner.register('adapter:application', AppAdapter); + + let adapter = store.adapterFor('person'); + + assert.ok(adapter instanceof AppAdapter, 'We found the adapter'); + assert.ok(didInstantiateAppAdapter, 'We instantiated the adapter'); + didInstantiateAppAdapter = false; + + let appAdapter = store.adapterFor('application'); + assert.ok(appAdapter instanceof AppAdapter, 'We found the correct adapter'); + assert.ok(!didInstantiateAppAdapter, 'We did not instantiate the adapter again'); + assert.ok(appAdapter === adapter, 'We fell back to the application adapter instance'); + }); + + test('we can specify a fallback adapter by name in place of the application adapter', async function(assert) { + store.adapter = '-rest'; + let { owner } = this; + + let didInstantiateRestAdapter = false; + + class RestAdapter extends TestAdapter { + didInit() { + didInstantiateRestAdapter = true; + } + } + owner.register('adapter:-rest', RestAdapter); + + let adapter = store.adapterFor('person'); + + assert.ok(adapter instanceof RestAdapter, 'We found the fallback -rest adapter for person'); + assert.ok(didInstantiateRestAdapter, 'We instantiated the adapter'); + didInstantiateRestAdapter = false; + + let appAdapter = store.adapterFor('application'); + + assert.ok( + appAdapter instanceof RestAdapter, + 'We found the fallback -rest adapter for application' + ); + assert.ok(!didInstantiateRestAdapter, 'We did not instantiate the adapter again'); + didInstantiateRestAdapter = false; + + let restAdapter = store.adapterFor('-rest'); + assert.ok(restAdapter instanceof RestAdapter, 'We found the correct adapter'); + assert.ok(!didInstantiateRestAdapter, 'We did not instantiate the adapter again'); + assert.ok( + restAdapter === adapter, + 'We fell back to the -rest adapter instance for the person adapters' + ); + assert.ok( + restAdapter === appAdapter, + 'We fell back to the -rest adapter instance for the application adapter' + ); + }); + + test('the application adapter has higher precedence than a fallback adapter defined via store.adapter', async function(assert) { + store.adapter = '-rest'; + let { owner } = this; + + let didInstantiateAppAdapter = false; + let didInstantiateRestAdapter = false; + + class AppAdapter extends TestAdapter { + didInit() { + didInstantiateAppAdapter = true; + } + } + + class RestAdapter extends TestAdapter { + didInit() { + didInstantiateRestAdapter = true; + } + } + + owner.register('adapter:application', AppAdapter); + owner.register('adapter:-rest', RestAdapter); + + let adapter = store.adapterFor('person'); + + assert.ok(adapter instanceof AppAdapter, 'We found the store specified fallback adapter'); + assert.ok( + !didInstantiateRestAdapter, + 'We did not instantiate the store.adapter (-rest) adapter' + ); + assert.ok(didInstantiateAppAdapter, 'We instantiated the application adapter'); + didInstantiateRestAdapter = false; + didInstantiateAppAdapter = false; + + let appAdapter = store.adapterFor('application'); + assert.ok(appAdapter instanceof AppAdapter, 'We found the correct adapter for application'); + assert.ok(!didInstantiateRestAdapter, 'We did not instantiate the store fallback adapter'); + assert.ok(!didInstantiateAppAdapter, 'We did not instantiate the application adapter again'); + assert.ok(appAdapter === adapter, 'We used the application adapter as the person adapter'); + didInstantiateRestAdapter = false; + didInstantiateAppAdapter = false; + + let restAdapter = store.adapterFor('-rest'); + assert.ok(restAdapter instanceof RestAdapter, 'We found the correct adapter for -rest'); + assert.ok(!didInstantiateAppAdapter, 'We did not instantiate the application adapter again'); + assert.ok(didInstantiateRestAdapter, 'We instantiated the fallback adapter'); + assert.ok(restAdapter !== appAdapter, `We did not use the application adapter instance`); + }); + + test('we can specify a fallback adapter by name in place of the application adapter', async function(assert) { + store.adapter = '-rest'; + let { owner } = this; + + let didInstantiateRestAdapter = false; + + class RestAdapter extends TestAdapter { + didInit() { + didInstantiateRestAdapter = true; + } + } + owner.register('adapter:-rest', RestAdapter); + + let adapter = store.adapterFor('application'); + + assert.ok(adapter instanceof RestAdapter, 'We found the adapter'); + assert.ok(didInstantiateRestAdapter, 'We instantiated the adapter'); + didInstantiateRestAdapter = false; + + let restAdapter = store.adapterFor('-rest'); + assert.ok(restAdapter instanceof RestAdapter, 'We found the correct adapter'); + assert.ok(!didInstantiateRestAdapter, 'We did not instantiate the adapter again'); + assert.ok( + restAdapter === adapter, + 'We fell back to the -rest adapter instance for the application adapter' + ); + }); + + test('When the per-type, application and specified fallback adapters do not exist, we fallback to the -json-api adapter', async function(assert) { + store.adapter = '-not-a-real-adapter'; + let { owner } = this; + + let didInstantiateAdapter = false; + + class JsonApiAdapter extends TestAdapter { + didInit() { + didInstantiateAdapter = true; + } + } + owner.unregister('adapter:-json-api'); + owner.register('adapter:-json-api', JsonApiAdapter); + + let adapter = store.adapterFor('person'); + + assert.ok(adapter instanceof JsonApiAdapter, 'We found the adapter'); + assert.ok(didInstantiateAdapter, 'We instantiated the adapter'); + didInstantiateAdapter = false; + + let appAdapter = store.adapterFor('application'); + + assert.ok( + appAdapter instanceof JsonApiAdapter, + 'We found the fallback -json-api adapter for application' + ); + assert.ok(!didInstantiateAdapter, 'We did not instantiate the adapter again'); + didInstantiateAdapter = false; + + let fallbackAdapter = store.adapterFor('-not-a-real-adapter'); + + assert.ok( + fallbackAdapter instanceof JsonApiAdapter, + 'We found the fallback -json-api adapter for application' + ); + assert.ok(!didInstantiateAdapter, 'We did not instantiate the adapter again'); + didInstantiateAdapter = false; + + let jsonApiAdapter = store.adapterFor('-json-api'); + assert.ok(jsonApiAdapter instanceof JsonApiAdapter, 'We found the correct adapter'); + assert.ok(!didInstantiateAdapter, 'We did not instantiate the adapter again'); + assert.ok( + jsonApiAdapter === appAdapter, + 'We fell back to the -json-api adapter instance for application' + ); + assert.ok( + jsonApiAdapter === fallbackAdapter, + 'We fell back to the -json-api adapter instance for the fallback -not-a-real-adapter' + ); + assert.ok( + jsonApiAdapter === adapter, + 'We fell back to the -json-api adapter instance for the per-type adapter' + ); + }); +}); diff --git a/tests/integration/store/serializer-for-test.js b/tests/integration/store/serializer-for-test.js new file mode 100644 index 00000000000..01878d67046 --- /dev/null +++ b/tests/integration/store/serializer-for-test.js @@ -0,0 +1,462 @@ +import { setupTest } from 'ember-qunit'; +import { module, test } from 'qunit'; +import Store from 'ember-data/store'; +import { run } from '@ember/runloop'; + +class TestAdapter { + constructor(args) { + Object.assign(this, args); + this.didInit(); + } + + didInit() {} + + static create(args) { + return new this(args); + } +} + +class TestSerializer { + constructor(args) { + Object.assign(this, args); + this.didInit(); + } + + didInit() {} + + static create(args) { + return new this(args); + } +} + +/* + Serializer Fallback Rules + + 1. per-type + 2. application + 3. Adapter.defaultSerializer + 4. serializer:-default (json-api serializer) + */ +module('integration/store - serializerFor', function(hooks) { + setupTest(hooks); + let store; + + hooks.beforeEach(function() { + let { owner } = this; + + store = owner.lookup('service:store'); + }); + + test('when no serializer is available we throw an error', async function(assert) { + let { owner } = this; + /* + serializer:-default is the "last chance" fallback and is + registered automatically as the json-api serializer. + unregistering it will cause serializerFor to return `undefined`. + */ + owner.unregister('serializer:-default'); + /* + we fallback to -json-api adapter by default when no other adapter is present. + This adapter specifies a defaultSerializer. We register our own to ensure + that this does not occur. + */ + class AppAdapter extends TestAdapter { + constructor() { + super(...arguments); + // ensure our adapter instance does not specify a fallback + // we use an empty string as that would cause `owner.lookup` to blow up if not guarded properly + // whereas `null` `undefined` `false` would not. + this.defaultSerializer = ''; + } + } + owner.register('adapter:application', AppAdapter); + + assert.expectAssertion(() => { + store.serializerFor('person'); + }, /No serializer was found for 'person' and no 'application', Adapter\.defaultSerializer, or '-default' serializer were found as fallbacks\./); + }); + + test('we find and instantiate the application serializer', async function(assert) { + let { owner } = this; + let didInstantiate = false; + + class AppSerializer extends TestSerializer { + didInit() { + didInstantiate = true; + } + } + + owner.register('serializer:application', AppSerializer); + + let serializer = store.serializerFor('application'); + + assert.ok(serializer instanceof AppSerializer, 'We found the correct serializer'); + assert.ok(didInstantiate, 'We instantiated the serializer'); + didInstantiate = false; + + let serializerAgain = store.serializerFor('application'); + + assert.ok(serializerAgain instanceof AppSerializer, 'We found the correct serializer'); + assert.ok(!didInstantiate, 'We did not instantiate the serializer again'); + assert.ok( + serializer === serializerAgain, + 'Repeated calls to serializerFor return the same instance' + ); + }); + + test('multiple stores do not share serializers', async function(assert) { + let { owner } = this; + let didInstantiate = false; + + class AppSerializer extends TestSerializer { + didInit() { + didInstantiate = true; + } + } + + owner.register('serializer:application', AppSerializer); + owner.register('service:other-store', Store); + + let otherStore = owner.lookup('service:other-store'); + let serializer = store.serializerFor('application'); + + assert.ok(serializer instanceof AppSerializer, 'We found the correct serializer'); + assert.ok(didInstantiate, 'We instantiated the serializer'); + didInstantiate = false; + + let otherSerializer = otherStore.serializerFor('application'); + assert.ok(otherSerializer instanceof AppSerializer, 'We found the correct serializer again'); + assert.ok(didInstantiate, 'We instantiated the other serializer'); + assert.ok(otherSerializer !== serializer, 'We have a different serializer instance'); + + // Ember 2.18 requires us to wrap destroy in a run. Use `await settled()` for newer versions. + run(() => otherStore.destroy()); + }); + + test('we can find and instantiate per-type serializers', async function(assert) { + let { owner } = this; + let didInstantiateAppSerializer = false; + let didInstantiatePersonSerializer = false; + + class AppSerializer extends TestSerializer { + didInit() { + didInstantiateAppSerializer = true; + } + } + + class PersonSerializer extends TestSerializer { + didInit() { + didInstantiatePersonSerializer = true; + } + } + + owner.register('serializer:application', AppSerializer); + owner.register('serializer:person', PersonSerializer); + + let serializer = store.serializerFor('person'); + + assert.ok(serializer instanceof PersonSerializer, 'We found the correct serializer'); + assert.ok(didInstantiatePersonSerializer, 'We instantiated the person serializer'); + assert.ok(!didInstantiateAppSerializer, 'We did not instantiate the application serializer'); + + let appSerializer = store.serializerFor('application'); + assert.ok(appSerializer instanceof AppSerializer, 'We found the correct serializer'); + assert.ok(didInstantiateAppSerializer, 'We instantiated the application serializer'); + assert.ok(appSerializer !== serializer, 'We have separate serializers'); + }); + + test('we fallback to the application serializer when a per-type serializer is not found', async function(assert) { + let { owner } = this; + let didInstantiateAppSerializer = false; + + class AppSerializer extends TestSerializer { + didInit() { + didInstantiateAppSerializer = true; + } + } + + owner.register('serializer:application', AppSerializer); + + let serializer = store.serializerFor('person'); + + assert.ok(serializer instanceof AppSerializer, 'We found the serializer'); + assert.ok(didInstantiateAppSerializer, 'We instantiated the serializer'); + didInstantiateAppSerializer = false; + + let appSerializer = store.serializerFor('application'); + assert.ok(appSerializer instanceof AppSerializer, 'We found the correct serializer'); + assert.ok(!didInstantiateAppSerializer, 'We did not instantiate the serializer again'); + assert.ok(appSerializer === serializer, 'We fell back to the application serializer instance'); + }); + + module('Adapter Fallback', function() { + test('we can specify a fallback serializer on the adapter when there is no application serializer', async function(assert) { + let { owner } = this; + let personAdapterDidInit = false; + let fallbackSerializerDidInit = false; + + class PersonAdapter extends TestAdapter { + constructor() { + super(...arguments); + this.defaultSerializer = '-fallback'; + } + + didInit() { + personAdapterDidInit = true; + } + } + class FallbackSerializer extends TestSerializer { + didInit() { + fallbackSerializerDidInit = true; + } + } + + owner.register('adapter:person', PersonAdapter); + owner.register('serializer:-fallback', FallbackSerializer); + + let serializer = store.serializerFor('person'); + + assert.ok(serializer instanceof FallbackSerializer, 'We found the serializer'); + assert.ok(personAdapterDidInit, 'We instantiated the adapter'); + assert.ok(fallbackSerializerDidInit, 'We instantiated the serializer'); + personAdapterDidInit = false; + fallbackSerializerDidInit = false; + + let fallbackSerializer = store.serializerFor('-fallback'); + assert.ok( + fallbackSerializer instanceof FallbackSerializer, + 'We found the correct serializer' + ); + assert.ok(!fallbackSerializerDidInit, 'We did not instantiate the serializer again'); + assert.ok(!personAdapterDidInit, 'We did not instantiate the adapter again'); + assert.ok( + fallbackSerializer === serializer, + 'We fell back to the fallback-serializer instance' + ); + }); + + test('specifying defaultSerializer on application serializer when there is a per-type serializer does not work', async function(assert) { + let { owner } = this; + let appAdapterDidInit = false; + let personAdapterDidInit = false; + let fallbackSerializerDidInit = false; + let defaultSerializerDidInit = false; + + class AppAdapter extends TestAdapter { + constructor() { + super(...arguments); + this.defaultSerializer = '-fallback'; + } + + didInit() { + appAdapterDidInit = true; + } + } + class PersonAdapter extends TestAdapter { + constructor() { + super(...arguments); + this.defaultSerializer = null; + } + + didInit() { + personAdapterDidInit = true; + } + } + class FallbackSerializer extends TestSerializer { + didInit() { + fallbackSerializerDidInit = true; + } + } + class DefaultSerializer extends TestSerializer { + didInit() { + defaultSerializerDidInit = true; + } + } + + owner.register('adapter:application', AppAdapter); + owner.register('adapter:person', PersonAdapter); + owner.register('serializer:-fallback', FallbackSerializer); + /* + serializer:-default is the "last chance" fallback and is + registered automatically as the json-api serializer. + */ + owner.unregister('serializer:-default'); + owner.register('serializer:-default', DefaultSerializer); + + let serializer = store.serializerFor('person'); + + assert.ok(serializer instanceof DefaultSerializer, 'We found the serializer'); + assert.ok(personAdapterDidInit, 'We instantiated the person adapter'); + assert.ok(!appAdapterDidInit, 'We did not instantiate the application adapter'); + assert.ok( + !fallbackSerializerDidInit, + 'We did not instantiate the application adapter fallback serializer' + ); + assert.ok(defaultSerializerDidInit, 'We instantiated the `-default` fallback serializer'); + personAdapterDidInit = false; + appAdapterDidInit = false; + fallbackSerializerDidInit = false; + defaultSerializerDidInit = false; + + let defaultSerializer = store.serializerFor('-default'); + assert.ok(defaultSerializer instanceof DefaultSerializer, 'We found the correct serializer'); + assert.ok(!defaultSerializerDidInit, 'We did not instantiate the serializer again'); + assert.ok(!appAdapterDidInit, 'We did not instantiate the application adapter'); + assert.ok( + !fallbackSerializerDidInit, + 'We did not instantiate the application adapter fallback serializer' + ); + assert.ok(!personAdapterDidInit, 'We did not instantiate the adapter again'); + assert.ok( + defaultSerializer === serializer, + 'We fell back to the fallback-serializer instance' + ); + }); + + test('specifying defaultSerializer on a fallback serializer when there is no per-type serializer does work', async function(assert) { + let { owner } = this; + let appAdapterDidInit = false; + let fallbackSerializerDidInit = false; + let defaultSerializerDidInit = false; + + class AppAdapter extends TestAdapter { + constructor() { + super(...arguments); + this.defaultSerializer = '-fallback'; + } + + didInit() { + appAdapterDidInit = true; + } + } + class FallbackSerializer extends TestSerializer { + didInit() { + fallbackSerializerDidInit = true; + } + } + class DefaultSerializer extends TestSerializer { + didInit() { + defaultSerializerDidInit = true; + } + } + + owner.register('adapter:application', AppAdapter); + owner.register('serializer:-fallback', FallbackSerializer); + /* + serializer:-default is the "last chance" fallback and is + registered automatically as the json-api serializer. + */ + owner.unregister('serializer:-default'); + owner.register('serializer:-default', DefaultSerializer); + + let serializer = store.serializerFor('person'); + + assert.ok(serializer instanceof FallbackSerializer, 'We found the serializer'); + assert.ok(appAdapterDidInit, 'We instantiated the fallback application adapter'); + assert.ok( + fallbackSerializerDidInit, + 'We instantiated the application adapter fallback defaultSerializer' + ); + assert.ok( + !defaultSerializerDidInit, + 'We did not instantiate the `-default` fallback serializer' + ); + appAdapterDidInit = false; + fallbackSerializerDidInit = false; + defaultSerializerDidInit = false; + + let fallbackSerializer = store.serializerFor('-fallback'); + assert.ok( + fallbackSerializer instanceof FallbackSerializer, + 'We found the correct serializer' + ); + assert.ok(!defaultSerializerDidInit, 'We did not instantiate the default serializer'); + assert.ok(!appAdapterDidInit, 'We did not instantiate the application adapter again'); + assert.ok( + !fallbackSerializerDidInit, + 'We did not instantiate the application adapter fallback serializer again' + ); + assert.ok( + fallbackSerializer === serializer, + 'We fell back to the fallback-serializer instance' + ); + }); + }); + + test('When the per-type, application and adapter specified fallback serializer do not exist, we fallback to the -default serializer', async function(assert) { + let { owner } = this; + let appAdapterDidInit = false; + let defaultSerializerDidInit = false; + + class AppAdapter extends TestAdapter { + constructor() { + super(...arguments); + this.defaultSerializer = '-not-a-real-fallback'; + } + + didInit() { + appAdapterDidInit = true; + } + } + class DefaultSerializer extends TestSerializer { + didInit() { + defaultSerializerDidInit = true; + } + } + + owner.register('adapter:application', AppAdapter); + /* + serializer:-default is the "last chance" fallback and is + registered automatically as the json-api serializer. + */ + owner.unregister('serializer:-default'); + owner.register('serializer:-default', DefaultSerializer); + + let serializer = store.serializerFor('person'); + + assert.ok(serializer instanceof DefaultSerializer, 'We found the serializer'); + assert.ok(appAdapterDidInit, 'We instantiated the fallback application adapter'); + assert.ok(defaultSerializerDidInit, 'We instantiated the `-default` fallback serializer'); + appAdapterDidInit = false; + defaultSerializerDidInit = false; + + let appSerializer = store.serializerFor('application'); + + assert.ok(appSerializer instanceof DefaultSerializer, 'We found the serializer'); + assert.ok(!appAdapterDidInit, 'We did not instantiate the application adapter again'); + assert.ok( + !defaultSerializerDidInit, + 'We did not instantiate the `-default` fallback serializer again' + ); + appAdapterDidInit = false; + defaultSerializerDidInit = false; + + let fallbackSerializer = store.serializerFor('-not-a-real-fallback'); + + assert.ok(fallbackSerializer instanceof DefaultSerializer, 'We found the serializer'); + assert.ok(!appAdapterDidInit, 'We did not instantiate the application adapter again'); + assert.ok( + !defaultSerializerDidInit, + 'We did not instantiate the `-default` fallback serializer again' + ); + appAdapterDidInit = false; + defaultSerializerDidInit = false; + + let defaultSerializer = store.serializerFor('-default'); + assert.ok(defaultSerializer instanceof DefaultSerializer, 'We found the correct serializer'); + assert.ok(!defaultSerializerDidInit, 'We did not instantiate the default serializer again'); + assert.ok(!appAdapterDidInit, 'We did not instantiate the application adapter again'); + assert.ok( + defaultSerializer === serializer, + 'We fell back to the -default serializer instance for the per-type serializer' + ); + assert.ok( + defaultSerializer === appSerializer, + 'We fell back to the -default serializer instance for the application serializer' + ); + assert.ok( + defaultSerializer === fallbackSerializer, + 'We fell back to the -default serializer instance for the adapter defaultSerializer' + ); + }); +}); diff --git a/tests/unit/store/lookup-test.js b/tests/unit/store/lookup-test.js deleted file mode 100644 index ac96f6ff363..00000000000 --- a/tests/unit/store/lookup-test.js +++ /dev/null @@ -1,164 +0,0 @@ -import { run } from '@ember/runloop'; -import setupStore from 'dummy/tests/helpers/store'; - -import { module, test } from 'qunit'; - -import DS from 'ember-data'; - -let store, env, applicationAdapter, applicationSerializer, Person; - -function resetStore() { - if (store) { - run(store, 'destroy'); - } - env = setupStore({ - adapter: '-rest', - person: Person, - }); - - env.registry.unregister('adapter:application'); - env.registry.unregister('serializer:application'); - - env.registry.optionsForType('serializer', { singleton: true }); - env.registry.optionsForType('adapter', { singleton: true }); - - store = env.store; -} - -function lookupAdapter(adapterName) { - return run(store, 'adapterFor', adapterName); -} - -function lookupSerializer(serializerName) { - return run(store, 'serializerFor', serializerName); -} - -function registerAdapter(adapterName, adapter) { - env.registry.register(`adapter:${adapterName}`, adapter); -} - -function registerSerializer(serializerName, serializer) { - env.registry.register(`serializer:${serializerName}`, serializer); -} - -module('unit/store/lookup - Managed Instance lookups', { - beforeEach() { - Person = DS.Model.extend(); - resetStore(); - env.registry.register('adapter:application', DS.Adapter.extend()); - env.registry.register('adapter:serializer', DS.Adapter.extend()); - - applicationAdapter = run(store, 'adapterFor', 'application'); - applicationSerializer = run(store, 'serializerFor', 'application'); - }, - - afterEach() { - run(store, 'destroy'); - }, -}); - -test('when the adapter does not exist for a type, the fallback is returned', assert => { - let personAdapter = lookupAdapter('person'); - - assert.strictEqual(personAdapter, applicationAdapter); -}); - -test('when the adapter for a type exists, returns that instead of the fallback', assert => { - registerAdapter('person', DS.Adapter.extend()); - let personAdapter = lookupAdapter('person'); - - assert.ok(personAdapter !== applicationAdapter); -}); - -test('when the serializer does not exist for a type, the fallback is returned', assert => { - let personSerializer = lookupSerializer('person'); - - assert.strictEqual(personSerializer, applicationSerializer); -}); - -test('when the serializer does exist for a type, the serializer is returned', assert => { - registerSerializer('person', DS.Serializer.extend()); - - let personSerializer = lookupSerializer('person'); - - assert.ok(personSerializer !== applicationSerializer); -}); - -test('adapter lookup order', assert => { - assert.expect(3); - - resetStore(); - - let personAdapter = lookupAdapter('person'); - - assert.strictEqual(personAdapter, lookupAdapter('-rest'), 'looks up the RESTAdapter first'); - resetStore(); - - registerAdapter('application', DS.RESTSerializer.extend()); - personAdapter = lookupAdapter('person'); - - assert.strictEqual( - personAdapter, - lookupAdapter('application'), - 'looks up application adapter before RESTAdapter if it exists' - ); - - resetStore(); - - registerAdapter('application', DS.RESTSerializer.extend()); - registerAdapter('person', DS.RESTSerializer.extend({ customThingy: true })); - - assert.ok( - lookupAdapter('person').get('customThingy'), - 'looks up type serializer before application' - ); -}); - -test('serializer lookup order', assert => { - resetStore(); - - let personSerializer = lookupSerializer('person'); - - assert.strictEqual(personSerializer, lookupSerializer('-rest')); - - resetStore(); - - registerSerializer('application', DS.RESTSerializer.extend()); - personSerializer = lookupSerializer('person'); - assert.strictEqual( - personSerializer, - lookupSerializer('application'), - 'looks up application before default' - ); - - resetStore(); - registerAdapter( - 'person', - DS.Adapter.extend({ - defaultSerializer: '-rest', - }) - ); - personSerializer = lookupSerializer('person'); - - assert.strictEqual( - personSerializer, - lookupSerializer('-rest'), - 'uses defaultSerializer on adapterFor("model") if application not defined' - ); - - resetStore(); - registerAdapter( - 'person', - DS.Adapter.extend({ - defaultSerializer: '-rest', - }) - ); - registerSerializer('application', DS.RESTSerializer.extend()); - registerSerializer('person', DS.JSONSerializer.extend({ customThingy: true })); - personSerializer = lookupSerializer('person'); - - assert.ok( - personSerializer.get('customThingy'), - 'uses the person serializer before any fallbacks if it is defined' - ); -});