From 3e36f674d6d5c9824ced511b08c0d6d4c16d4118 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 16 Dec 2024 23:28:18 -0800 Subject: [PATCH] more test fixes --- .../build-config/src/deprecation-versions.ts | 2 +- packages/graph/eslint.config.mjs | 3 +- .../-private/debug/assert-polymorphic-type.ts | 221 +++++++++++++----- packages/graph/vite.config.mjs | 1 + packages/model/src/-private/belongs-to.ts | 48 +++- packages/model/src/-private/has-many.ts | 11 +- packages/model/src/-private/many-array.ts | 35 ++- .../model/src/-private/promise-many-array.ts | 2 +- .../non-dasherized-lookups-test.js | 1 - tests/main/tests/integration/inverse-test.js | 2 +- .../records/relationship-changes-test.js | 6 +- .../integration/references/belongs-to-test.js | 2 +- .../integration/references/has-many-test.js | 8 +- .../relationships/belongs-to-test.js | 2 +- .../collection/mutating-has-many-test.ts | 15 +- .../relationships/has-many-test.js | 12 +- .../relationships/one-to-one-test.js | 14 +- .../polymorphic-mixins-belongs-to-test.js | 2 +- .../relationships/promise-many-array-test.js | 13 +- .../unit/model/relationships/has-many-test.js | 65 +++--- 20 files changed, 333 insertions(+), 132 deletions(-) diff --git a/packages/build-config/src/deprecation-versions.ts b/packages/build-config/src/deprecation-versions.ts index 209d0d56025..80d5148b4d3 100644 --- a/packages/build-config/src/deprecation-versions.ts +++ b/packages/build-config/src/deprecation-versions.ts @@ -701,7 +701,7 @@ export const DEPRECATE_NON_EXPLICIT_POLYMORPHISM = '4.7'; * @until 6.0 * @public */ -export const DEPRECATE_MANY_ARRAY_DUPLICATES = '5.3'; +export const DEPRECATE_MANY_ARRAY_DUPLICATES = '4.12'; // '5.3'; /** * **id: ember-data:deprecate-non-strict-types** diff --git a/packages/graph/eslint.config.mjs b/packages/graph/eslint.config.mjs index 63296bac802..c42683457ca 100644 --- a/packages/graph/eslint.config.mjs +++ b/packages/graph/eslint.config.mjs @@ -2,6 +2,7 @@ import { globalIgnores } from '@warp-drive/internal-config/eslint/ignore.js'; import * as node from '@warp-drive/internal-config/eslint/node.js'; import * as typescript from '@warp-drive/internal-config/eslint/typescript.js'; +import { externals } from './vite.config.mjs'; /** @type {import('eslint').Linter.FlatConfig[]} */ export default [ @@ -11,7 +12,7 @@ export default [ // browser (js/ts) ================ typescript.browser({ srcDirs: ['src'], - allowedImports: ['@ember/debug'], + allowedImports: externals, }), // node (module) ================ diff --git a/packages/graph/src/-private/debug/assert-polymorphic-type.ts b/packages/graph/src/-private/debug/assert-polymorphic-type.ts index db9cb188bc6..6b432ed4c4c 100644 --- a/packages/graph/src/-private/debug/assert-polymorphic-type.ts +++ b/packages/graph/src/-private/debug/assert-polymorphic-type.ts @@ -1,11 +1,34 @@ /* eslint-disable @typescript-eslint/no-shadow */ -import type { CacheCapabilitiesManager } from '@ember-data/store/types'; +import type Mixin from '@ember/object/mixin'; + +import type Store from '@ember-data/store'; +import type { CacheCapabilitiesManager, ModelSchema } from '@ember-data/store/types'; +import { DEPRECATE_NON_EXPLICIT_POLYMORPHISM } from '@warp-drive/build-config/deprecations'; import { DEBUG } from '@warp-drive/build-config/env'; import { assert } from '@warp-drive/build-config/macros'; import type { StableRecordIdentifier } from '@warp-drive/core-types'; import { isLegacyField, isRelationshipField, temporaryConvertToLegacy, type UpgradedMeta } from '../-edge-definition'; +type Model = ModelSchema; + +// A pile of soft-lies to deal with mixin APIs +type ModelWithMixinApis = Model & { + isModel?: boolean; + __isMixin?: boolean; + __mixin: Mixin; + PrototypeMixin: Mixin; + detect: (mixin: Model | Mixin | ModelWithMixinApis) => boolean; + prototype: Model; + [Symbol.hasInstance](model: Model): true; +}; + +function assertModelSchemaIsModel( + schema: ModelSchema | Model | ModelWithMixinApis +): asserts schema is ModelWithMixinApis { + assert(`Expected Schema to be an instance of Model`, 'isModel' in schema && schema.isModel === true); +} + /* Assert that `addedRecord` has a valid type so it can be added to the relationship of the `record`. @@ -27,6 +50,21 @@ let assertPolymorphicType: ( let assertInheritedSchema: (definition: UpgradedMeta, type: string) => void; if (DEBUG) { + const checkPolymorphic = function checkPolymorphic(modelClass: ModelSchema, addedModelClass: ModelSchema) { + assertModelSchemaIsModel(modelClass); + assertModelSchemaIsModel(addedModelClass); + + if (modelClass.__isMixin) { + return ( + modelClass.__mixin.detect(addedModelClass.PrototypeMixin) || + // handle native class extension e.g. `class Post extends Model.extend(Commentable) {}` + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + modelClass.__mixin.detect(Object.getPrototypeOf(addedModelClass).PrototypeMixin) + ); + } + return addedModelClass.prototype instanceof modelClass || modelClass.detect(addedModelClass); + }; + function validateSchema(definition: UpgradedMeta, meta: PrintConfig) { const errors = new Map(); @@ -61,9 +99,9 @@ if (DEBUG) { kind: string; options: { as?: string; - async: boolean; + async?: boolean; polymorphic?: boolean; - inverse: string | null; + inverse?: string | null; }; }; type RelationshipSchemaError = 'name' | 'type' | 'kind' | 'as' | 'async' | 'polymorphic' | 'inverse'; @@ -211,69 +249,142 @@ if (DEBUG) { if (parentDefinition.inverseIsImplicit) { return; } + let asserted = false; + if (parentDefinition.isPolymorphic) { - let meta = store.schema.fields(addedIdentifier).get(parentDefinition.inverseKey); - assert( - `No '${parentDefinition.inverseKey}' field exists on '${ - addedIdentifier.type - }'. To use this type in the polymorphic relationship '${parentDefinition.inverseType}.${ - parentDefinition.key - }' the relationships schema definition for ${addedIdentifier.type} should include:${expectedSchema( - parentDefinition - )}`, - meta - ); - assert( - `Expected the field ${parentDefinition.inverseKey} to be a relationship`, - meta && isRelationshipField(meta) - ); - meta = isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta); + const rawMeta = store.schema.fields(addedIdentifier).get(parentDefinition.inverseKey); assert( - `You should not specify both options.as and options.inverse as null on ${addedIdentifier.type}.${parentDefinition.inverseKey}, as if there is no inverse field there is no abstract type to conform to. You may have intended for this relationship to be polymorphic, or you may have mistakenly set inverse to null.`, - !(meta.options.inverse === null && meta?.options.as?.length) - ); - const errors = validateSchema(parentDefinition, meta); - assert( - `The schema for the relationship '${parentDefinition.inverseKey}' on '${ - addedIdentifier.type - }' type does not correctly implement '${parentDefinition.type}' and thus cannot be assigned to the '${ - parentDefinition.key - }' relationship in '${ - parentIdentifier.type - }'. If using this record in this polymorphic relationship is desired, correct the errors in the schema shown below:${printSchema( - meta, - errors - )}`, - errors.size === 0 + `Expected to find a relationship field schema for ${parentDefinition.inverseKey} on ${addedIdentifier.type} but none was found`, + !rawMeta || isRelationshipField(rawMeta) ); + const meta = rawMeta && (isLegacyField(rawMeta) ? rawMeta : temporaryConvertToLegacy(rawMeta)); + + if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) { + if (meta?.options?.as) { + asserted = true; + assert( + `No '${parentDefinition.inverseKey}' field exists on '${addedIdentifier.type}'. To use this type in the polymorphic relationship '${parentDefinition.inverseType}.${parentDefinition.key}' the relationships schema definition for ${addedIdentifier.type} should include:${expectedSchema(parentDefinition)}`, + meta + ); + assert( + `You should not specify both options.as and options.inverse as null on ${addedIdentifier.type}.${parentDefinition.inverseKey}, as if there is no inverse field there is no abstract type to conform to. You may have intended for this relationship to be polymorphic, or you may have mistakenly set inverse to null.`, + !(meta.options.inverse === null && meta?.options.as?.length > 0) + ); + const errors = validateSchema(parentDefinition, meta); + assert( + `The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not correctly implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. If using this record in this polymorphic relationship is desired, correct the errors in the schema shown below:${printSchema(meta, errors)}`, + errors.size === 0 + ); + } + } else { + assert( + `No '${parentDefinition.inverseKey}' field exists on '${ + addedIdentifier.type + }'. To use this type in the polymorphic relationship '${parentDefinition.inverseType}.${ + parentDefinition.key + }' the relationships schema definition for ${addedIdentifier.type} should include:${expectedSchema( + parentDefinition + )}`, + meta + ); + assert( + `Expected the field ${parentDefinition.inverseKey} to be a relationship`, + meta && isRelationshipField(meta) + ); + assert( + `You should not specify both options.as and options.inverse as null on ${addedIdentifier.type}.${parentDefinition.inverseKey}, as if there is no inverse field there is no abstract type to conform to. You may have intended for this relationship to be polymorphic, or you may have mistakenly set inverse to null.`, + !(meta.options.inverse === null && meta?.options.as?.length) + ); + const errors = validateSchema(parentDefinition, meta); + assert( + `The schema for the relationship '${parentDefinition.inverseKey}' on '${ + addedIdentifier.type + }' type does not correctly implement '${parentDefinition.type}' and thus cannot be assigned to the '${ + parentDefinition.key + }' relationship in '${ + parentIdentifier.type + }'. If using this record in this polymorphic relationship is desired, correct the errors in the schema shown below:${printSchema( + meta, + errors + )}`, + errors.size === 0 + ); + } } else if (addedIdentifier.type !== parentDefinition.type) { // if we are not polymorphic // then the addedIdentifier.type must be the same as the parentDefinition.type - let meta = store.schema.fields(addedIdentifier).get(parentDefinition.inverseKey); + const rawMeta = store.schema.fields(addedIdentifier).get(parentDefinition.inverseKey); assert( - `Expected the field ${parentDefinition.inverseKey} to be a relationship`, - !meta || isRelationshipField(meta) + `Expected to find a relationship field schema for ${parentDefinition.inverseKey} on ${addedIdentifier.type} but none was found`, + !rawMeta || isRelationshipField(rawMeta) ); - meta = meta && (isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta)); - if (meta?.options.as === parentDefinition.type) { - // inverse is likely polymorphic but missing the polymorphic flag - let meta = store.schema.fields({ type: parentDefinition.inverseType }).get(parentDefinition.key); - assert(`Expected the field ${parentDefinition.key} to be a relationship`, meta && isRelationshipField(meta)); - meta = isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta); - const errors = validateSchema(definitionWithPolymorphic(inverseDefinition(parentDefinition)), meta); - assert( - `The '<${addedIdentifier.type}>.${ - parentDefinition.inverseKey - }' relationship cannot be used polymorphically because '<${parentDefinition.inverseType}>.${ - parentDefinition.key - } is not a polymorphic relationship. To use this relationship in a polymorphic manner, fix the following schema issues on the relationships schema for '${ - parentDefinition.inverseType - }':${printSchema(meta, errors)}` - ); + const meta = rawMeta && (isLegacyField(rawMeta) ? rawMeta : temporaryConvertToLegacy(rawMeta)); + + if (!DEPRECATE_NON_EXPLICIT_POLYMORPHISM) { + if (meta?.options.as === parentDefinition.type) { + // inverse is likely polymorphic but missing the polymorphic flag + const inverseMeta = store.schema.fields({ type: parentDefinition.inverseType }).get(parentDefinition.key); + assert( + `Expected to find a relationship field schema for ${parentDefinition.inverseKey} on ${addedIdentifier.type} but none was found`, + inverseMeta && isRelationshipField(inverseMeta) + ); + const legacyInverseMeta = + inverseMeta && (isLegacyField(inverseMeta) ? inverseMeta : temporaryConvertToLegacy(inverseMeta)); + const errors = validateSchema( + definitionWithPolymorphic(inverseDefinition(parentDefinition)), + legacyInverseMeta + ); + assert( + `The '<${addedIdentifier.type}>.${parentDefinition.inverseKey}' relationship cannot be used polymorphically because '<${parentDefinition.inverseType}>.${parentDefinition.key} is not a polymorphic relationship. To use this relationship in a polymorphic manner, fix the following schema issues on the relationships schema for '${parentDefinition.inverseType}':${printSchema(legacyInverseMeta, errors)}` + ); + } else { + assert( + `The '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. If this relationship should be polymorphic, mark ${parentDefinition.inverseType}.${parentDefinition.key} as \`polymorphic: true\` and ${addedIdentifier.type}.${parentDefinition.inverseKey} as implementing it via \`as: '${parentDefinition.type}'\`.` + ); + } } else { assert( - `The '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. If this relationship should be polymorphic, mark ${parentDefinition.inverseType}.${parentDefinition.key} as \`polymorphic: true\` and ${addedIdentifier.type}.${parentDefinition.inverseKey} as implementing it via \`as: '${parentDefinition.type}'\`.` + `Expected the field ${parentDefinition.inverseKey} to be a relationship`, + !meta || isRelationshipField(meta) ); + const legacyMeta = meta && (isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta)); + if (legacyMeta?.options.as === parentDefinition.type) { + // inverse is likely polymorphic but missing the polymorphic flag + let meta = store.schema.fields({ type: parentDefinition.inverseType }).get(parentDefinition.key); + assert(`Expected the field ${parentDefinition.key} to be a relationship`, meta && isRelationshipField(meta)); + meta = isLegacyField(meta) ? meta : temporaryConvertToLegacy(meta); + const errors = validateSchema(definitionWithPolymorphic(inverseDefinition(parentDefinition)), meta); + assert( + `The '<${addedIdentifier.type}>.${ + parentDefinition.inverseKey + }' relationship cannot be used polymorphically because '<${parentDefinition.inverseType}>.${ + parentDefinition.key + } is not a polymorphic relationship. To use this relationship in a polymorphic manner, fix the following schema issues on the relationships schema for '${ + parentDefinition.inverseType + }':${printSchema(meta, errors)}` + ); + } else { + assert( + `The '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. If this relationship should be polymorphic, mark ${parentDefinition.inverseType}.${parentDefinition.key} as \`polymorphic: true\` and ${addedIdentifier.type}.${parentDefinition.inverseKey} as implementing it via \`as: '${parentDefinition.type}'\`.` + ); + } + } + } + + if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) { + if (!asserted) { + const storeService = (store as unknown as { _store: Store })._store; + const addedModelName = addedIdentifier.type; + const parentModelName = parentIdentifier.type; + const key = parentDefinition.key; + const relationshipModelName = parentDefinition.type; + const relationshipClass = storeService.modelFor(relationshipModelName); + const addedClass = storeService.modelFor(addedModelName); + + const assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`; + const isPolymorphic = checkPolymorphic(relationshipClass, addedClass); + + assert(assertionMessage, isPolymorphic); } } }; diff --git a/packages/graph/vite.config.mjs b/packages/graph/vite.config.mjs index 7936563a474..a548ae349de 100644 --- a/packages/graph/vite.config.mjs +++ b/packages/graph/vite.config.mjs @@ -1,6 +1,7 @@ import { createConfig } from '@warp-drive/internal-config/vite/config.js'; export const externals = [ + '@ember/object/mixin', // type only '@ember/debug', // assert, deprecate ]; diff --git a/packages/model/src/-private/belongs-to.ts b/packages/model/src/-private/belongs-to.ts index aed99397220..f272596952c 100644 --- a/packages/model/src/-private/belongs-to.ts +++ b/packages/model/src/-private/belongs-to.ts @@ -2,9 +2,11 @@ import { deprecate, warn } from '@ember/debug'; import { computed } from '@ember/object'; import { + DEPRECATE_NON_STRICT_TYPES, DEPRECATE_RELATIONSHIPS_WITHOUT_ASYNC, DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE, DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE, + DISABLE_6X_DEPRECATIONS, } from '@warp-drive/build-config/deprecations'; import { DEBUG } from '@warp-drive/build-config/env'; import { assert } from '@warp-drive/build-config/macros'; @@ -13,7 +15,8 @@ import { RecordStore } from '@warp-drive/core-types/symbols'; import { lookupLegacySupport } from './legacy-relationships-support'; import type { MinimalLegacyRecord } from './model-methods'; -import { isElementDescriptor, normalizeModelName } from './util'; +import { isElementDescriptor } from './util'; +import { dasherize, singularize } from '@ember-data/request-utils/string'; /** @module @ember-data/model */ @@ -38,6 +41,36 @@ export type NoNull = Exclude; // eslint-disable-next-line @typescript-eslint/no-unused-vars export type RelationshipDecorator = (target: This, key: string, desc?: PropertyDescriptor) => void; // BelongsToDecoratorObject; +function normalizeType(type: string) { + if (DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) { + if (!type) { + return; + } + } + + if (DEPRECATE_NON_STRICT_TYPES) { + const result = singularize(dasherize(type)); + + deprecate( + `The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`, + /* inline-macro-config */ DISABLE_6X_DEPRECATIONS ? true : result === type, + { + id: 'ember-data:deprecate-non-strict-types', + until: '6.0', + for: 'ember-data', + since: { + available: '4.13', + enabled: '5.3', + }, + } + ); + + return result; + } + + return type; +} + function _belongsTo( type: string, options: RelationshipOptions @@ -116,7 +149,7 @@ function _belongsTo( } const meta = { - type: normalizeModelName(type), + type: normalizeType(type), options: opts, kind: 'belongsTo', name: '', @@ -354,11 +387,16 @@ export function belongsTo( type?: TypeFromInstance>, options?: RelationshipOptions ): RelationshipDecorator { - if (DEBUG) { + if (!DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) { assert( - `belongsTo must be invoked with a type and options. Did you mean \`@belongsTo(${type}, { async: false, inverse: null })\`?`, + `belongsTo must be invoked with a type and options. Did you mean \`@belongsTo(, { async: false, inverse: null })\`?`, !isElementDescriptor(arguments as unknown as unknown[]) ); + return _belongsTo(type!, options!); + } else { + return isElementDescriptor(arguments as unknown as any[]) + ? // @ts-expect-error the inbound signature is strict to convince the user to use the non-deprecated signature + (_belongsTo()(...arguments) as RelationshipDecorator) + : _belongsTo(type!, options!); } - return _belongsTo(type!, options!); } diff --git a/packages/model/src/-private/has-many.ts b/packages/model/src/-private/has-many.ts index b1ab84bebbb..d8c72495a78 100644 --- a/packages/model/src/-private/has-many.ts +++ b/packages/model/src/-private/has-many.ts @@ -337,11 +337,16 @@ export function hasMany( type?: TypeFromInstance>, options?: RelationshipOptions ): RelationshipDecorator { - if (DEBUG) { + if (!DEPRECATE_RELATIONSHIPS_WITHOUT_TYPE) { assert( - `hasMany must be invoked with a type and options. Did you mean \`@hasMany(${type}, { async: false, inverse: null })\`?`, + `hasMany must be invoked with a type and options. Did you mean \`@hasMany(, { async: false, inverse: null })\`?`, !isElementDescriptor(arguments as unknown as unknown[]) ); + return _hasMany(type!, options!); + } else { + return isElementDescriptor(arguments as unknown as any[]) + ? // @ts-expect-error the inbound signature is strict to convince the user to use the non-deprecated signature + (_hasMany()(...arguments) as RelationshipDecorator) + : _hasMany(type!, options!); } - return _hasMany(type!, options!); } diff --git a/packages/model/src/-private/many-array.ts b/packages/model/src/-private/many-array.ts index 7ec3b97396e..39d647cce71 100644 --- a/packages/model/src/-private/many-array.ts +++ b/packages/model/src/-private/many-array.ts @@ -17,7 +17,11 @@ import { import type { BaseFinderOptions, ModelSchema } from '@ember-data/store/types'; import type { Signal } from '@ember-data/tracking/-private'; import { addToTransaction } from '@ember-data/tracking/-private'; -import { DEPRECATE_MANY_ARRAY_DUPLICATES, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations'; +import { + DEPRECATE_MANY_ARRAY_DUPLICATES, + DEPRECATE_PROMISE_PROXIES, + DISABLE_6X_DEPRECATIONS, +} from '@warp-drive/build-config/deprecations'; import { assert } from '@warp-drive/build-config/macros'; import type { StableRecordIdentifier } from '@warp-drive/core-types'; import type { Cache } from '@warp-drive/core-types/cache'; @@ -485,10 +489,39 @@ function extractIdentifiersFromRecords(records: OpaqueRecordInstance[]): StableR } function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | OpaqueRecordInstance) { + if (DEPRECATE_PROMISE_PROXIES) { + if (isPromiseRecord(recordOrPromiseRecord)) { + let content = recordOrPromiseRecord.content; + assert( + 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo relationship.', + content !== undefined && content !== null + ); + deprecate( + `You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`, + false, + { + id: 'ember-data:deprecate-promise-proxies', + until: '5.0', + since: { + enabled: '4.7', + available: '4.7', + }, + for: 'ember-data', + } + ); + assertRecordPassedToHasMany(content); + return recordIdentifierFor(content); + } + } + assertRecordPassedToHasMany(recordOrPromiseRecord); return recordIdentifierFor(recordOrPromiseRecord); } +function isPromiseRecord(record: PromiseProxyRecord | OpaqueRecordInstance): record is PromiseProxyRecord { + return Boolean(typeof record === 'object' && record && 'then' in record); +} + function assertNoDuplicates( collection: RelatedCollection, target: StableRecordIdentifier[], diff --git a/packages/model/src/-private/promise-many-array.ts b/packages/model/src/-private/promise-many-array.ts index 5d906fbd23c..739195c1801 100644 --- a/packages/model/src/-private/promise-many-array.ts +++ b/packages/model/src/-private/promise-many-array.ts @@ -259,7 +259,7 @@ if (DEPRECATE_COMPUTED_CHAINS) { return this.content?.length && this.content; }, }; - compat(desc); + compat(PromiseManyArray.prototype, '[]', desc); // ember-source < 3.23 (e.g. 3.20 lts) // requires that the tag `'[]'` be notified diff --git a/tests/main/tests/integration/backwards-compat/non-dasherized-lookups-test.js b/tests/main/tests/integration/backwards-compat/non-dasherized-lookups-test.js index 6149fb62879..2cf3f2589ba 100644 --- a/tests/main/tests/integration/backwards-compat/non-dasherized-lookups-test.js +++ b/tests/main/tests/integration/backwards-compat/non-dasherized-lookups-test.js @@ -50,7 +50,6 @@ module( }, }); - debugger; const postNote = await store.findRecord('postNote', '1'); assert.strictEqual(postNote.name, 'Ember Data', 'record found'); diff --git a/tests/main/tests/integration/inverse-test.js b/tests/main/tests/integration/inverse-test.js index b2e223f8e8a..217ff318a38 100644 --- a/tests/main/tests/integration/inverse-test.js +++ b/tests/main/tests/integration/inverse-test.js @@ -228,7 +228,7 @@ module('integration/inverse-test - inverseFor', function (hooks) { const user = store.modelFor('user'); assert.expectAssertion(() => { user.inverseFor('job', store); - }, /Assertion Failed: You defined the 'job' relationship on model:user, but you defined the inverse relationships of type model:job multiple times/i); + }, /You defined the 'job' relationship on model:user, but you defined the inverse relationships of type model:job multiple times/i); } ); } diff --git a/tests/main/tests/integration/records/relationship-changes-test.js b/tests/main/tests/integration/records/relationship-changes-test.js index 672566eb65f..c6f9ebcd1fc 100644 --- a/tests/main/tests/integration/records/relationship-changes-test.js +++ b/tests/main/tests/integration/records/relationship-changes-test.js @@ -67,9 +67,9 @@ module('integration/records/relationship-changes - Relationship changes', functi deprecatedTest( 'Calling push with relationship recalculates computed alias property if the relationship was empty and is added to', - { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, + { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 2 }, function (assert) { - assert.expect(1); + assert.expect(2); const store = this.owner.lookup('service:store'); @@ -122,7 +122,7 @@ module('integration/records/relationship-changes - Relationship changes', functi 'Calling push with relationship recalculates computed alias property to firstObject if the relationship was empty and is added to', { id: 'ember-data:deprecate-promise-many-array-behaviors', until: '5.0', count: 1 }, function (assert) { - assert.expect(2); + assert.expect(3); const store = this.owner.lookup('service:store'); diff --git a/tests/main/tests/integration/references/belongs-to-test.js b/tests/main/tests/integration/references/belongs-to-test.js index c6c6a804381..bed38c7385c 100644 --- a/tests/main/tests/integration/references/belongs-to-test.js +++ b/tests/main/tests/integration/references/belongs-to-test.js @@ -494,7 +494,7 @@ module('integration/references/belongs-to', function (hooks) { await familyReference.push(anotherPerson); }, DEPRECATE_NON_EXPLICIT_POLYMORPHISM - ? "Assertion Failed: The 'person' type does not implement 'family' and thus cannot be assigned to the 'family' relationship in 'person'. Make it a descendant of 'family' or use a mixin of the same name." + ? "The 'person' type does not implement 'family' and thus cannot be assigned to the 'family' relationship in 'person'. Make it a descendant of 'family' or use a mixin of the same name." : "The 'person' type does not implement 'family' and thus cannot be assigned to the 'family' relationship in 'person'. If this relationship should be polymorphic, mark person.family as `polymorphic: true` and person.persons as implementing it via `as: 'family'`." ); }); diff --git a/tests/main/tests/integration/references/has-many-test.js b/tests/main/tests/integration/references/has-many-test.js index c04e4f4c08d..eb1b8561e9b 100755 --- a/tests/main/tests/integration/references/has-many-test.js +++ b/tests/main/tests/integration/references/has-many-test.js @@ -334,10 +334,10 @@ module('integration/references/has-many', function (hooks) { await assert.expectAssertion( async () => { - await petsReference.push([{ data: { type: 'person', id: '1' } }]); + await petsReference.push([{ type: 'person', id: '1' }]); }, DEPRECATE_NON_EXPLICIT_POLYMORPHISM - ? "Assertion Failed: The 'person' type does not implement 'animal' and thus cannot be assigned to the 'pets' relationship in 'person'. Make it a descendant of 'animal' or use a mixin of the same name." + ? "The 'person' type does not implement 'animal' and thus cannot be assigned to the 'pets' relationship in 'person'. Make it a descendant of 'animal' or use a mixin of the same name." : "The 'person' type does not implement 'animal' and thus cannot be assigned to the 'pets' relationship in 'person'. If this relationship should be polymorphic, mark person.pets as `polymorphic: true` and person.owner as implementing it via `as: 'animal'`." ); }); @@ -411,8 +411,8 @@ module('integration/references/has-many', function (hooks) { const payload = { data: [ - { data: { type: 'person', id: '1', attributes: { name: 'Vito' } } }, - { data: { type: 'person', id: '2', attributes: { name: 'Michael' } } }, + { type: 'person', id: '1', attributes: { name: 'Vito' } }, + { type: 'person', id: '2', attributes: { name: 'Michael' } }, ], }; diff --git a/tests/main/tests/integration/relationships/belongs-to-test.js b/tests/main/tests/integration/relationships/belongs-to-test.js index 6e2ff46b46e..df839881df4 100644 --- a/tests/main/tests/integration/relationships/belongs-to-test.js +++ b/tests/main/tests/integration/relationships/belongs-to-test.js @@ -587,7 +587,7 @@ module('integration/relationship/belongs_to Belongs-To Relationships', function post.user = comment; }, DEPRECATE_NON_EXPLICIT_POLYMORPHISM - ? "Assertion Failed: The 'comment' type does not implement 'user' and thus cannot be assigned to the 'user' relationship in 'post'. Make it a descendant of 'user' or use a mixin of the same name." + ? "The 'comment' type does not implement 'user' and thus cannot be assigned to the 'user' relationship in 'post'. Make it a descendant of 'user' or use a mixin of the same name." : "The 'comment' type does not implement 'user' and thus cannot be assigned to the 'user' relationship in 'post'. If this relationship should be polymorphic, mark message.user as `polymorphic: true` and comment.messages as implementing it via `as: 'user'`." ); } diff --git a/tests/main/tests/integration/relationships/collection/mutating-has-many-test.ts b/tests/main/tests/integration/relationships/collection/mutating-has-many-test.ts index 169ed675f40..6037969b471 100644 --- a/tests/main/tests/integration/relationships/collection/mutating-has-many-test.ts +++ b/tests/main/tests/integration/relationships/collection/mutating-has-many-test.ts @@ -8,19 +8,13 @@ import type { ManyArray } from '@ember-data/model'; import Model, { attr, hasMany } from '@ember-data/model'; import type Store from '@ember-data/store'; import { recordIdentifierFor } from '@ember-data/store'; -import { DEPRECATE_MANY_ARRAY_DUPLICATES } from '@warp-drive/build-config/deprecations'; +import { DEPRECATE_MANY_ARRAY_DUPLICATES, DISABLE_6X_DEPRECATIONS } from '@warp-drive/build-config/deprecations'; import type { ExistingResourceIdentifierObject } from '@warp-drive/core-types/spec/json-api-raw'; import { Type } from '@warp-drive/core-types/symbols'; import type { ReactiveContext } from '../../../helpers/reactive-context'; import { reactiveContext } from '../../../helpers/reactive-context'; -let IS_DEPRECATE_MANY_ARRAY_DUPLICATES = false; - -if (DEPRECATE_MANY_ARRAY_DUPLICATES) { - IS_DEPRECATE_MANY_ARRAY_DUPLICATES = true; -} - class User extends Model { @attr declare name: string; @hasMany('user', { async: false, inverse: 'friends' }) declare friends: ManyArray; @@ -214,8 +208,11 @@ async function applyMutation(assert: Assert, store: Store, record: User, mutatio const result = generateAppliedMutation(store, record, mutation); const initialIds = record.friends.map((f) => f.id).join(','); - const shouldError = result.hasDuplicates && !IS_DEPRECATE_MANY_ARRAY_DUPLICATES; - const shouldDeprecate = result.hasDuplicates && IS_DEPRECATE_MANY_ARRAY_DUPLICATES; + const shouldError = result.hasDuplicates && /* inline-macro-config */ !DEPRECATE_MANY_ARRAY_DUPLICATES; + const shouldDeprecate = + result.hasDuplicates && + /* inline-macro-config */ DEPRECATE_MANY_ARRAY_DUPLICATES && + /* inline-macro-config */ !DISABLE_6X_DEPRECATIONS; const expected = shouldError ? result.unchanged : result.deduped; try { diff --git a/tests/main/tests/integration/relationships/has-many-test.js b/tests/main/tests/integration/relationships/has-many-test.js index eafabe12564..85e02673651 100644 --- a/tests/main/tests/integration/relationships/has-many-test.js +++ b/tests/main/tests/integration/relationships/has-many-test.js @@ -1914,8 +1914,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( assert.strictEqual( e.message, DEPRECATE_NON_EXPLICIT_POLYMORPHISM - ? "Assertion Failed: The 'post' type does not implement 'comment' and thus cannot be assigned to the 'comments' relationship in 'post'. Make it a descendant of 'comment' or use a mixin of the same name." - : "Assertion Failed: The 'post' type does not implement 'comment' and thus cannot be assigned to the 'comments' relationship in 'post'. If this relationship should be polymorphic, mark post.comments as `polymorphic: true` and post.message as implementing it via `as: 'comment'`.", + ? "The 'post' type does not implement 'comment' and thus cannot be assigned to the 'comments' relationship in 'post'. Make it a descendant of 'comment' or use a mixin of the same name." + : "The 'post' type does not implement 'comment' and thus cannot be assigned to the 'comments' relationship in 'post'. If this relationship should be polymorphic, mark post.comments as `polymorphic: true` and post.message as implementing it via `as: 'comment'`.", 'should throw' ); } @@ -1991,8 +1991,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( user.messages.push(anotherUser); }, DEPRECATE_NON_EXPLICIT_POLYMORPHISM - ? "Assertion Failed: The 'user' type does not implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. Make it a descendant of 'message' or use a mixin of the same name." - : `Assertion Failed: The schema for the relationship 'user' on 'user' type does not correctly implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. If using this record in this polymorphic relationship is desired, correct the errors in the schema shown below: + ? "The 'user' type does not implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. Make it a descendant of 'message' or use a mixin of the same name." + : `The schema for the relationship 'user' on 'user' type does not correctly implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. If using this record in this polymorphic relationship is desired, correct the errors in the schema shown below: \`\`\` { @@ -2125,8 +2125,8 @@ module('integration/relationships/has_many - Has-Many Relationships', function ( user.messages.push(anotherUser); }, DEPRECATE_NON_EXPLICIT_POLYMORPHISM - ? "Assertion Failed: The 'user' type does not implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. Make it a descendant of 'message' or use a mixin of the same name." - : `Assertion Failed: No 'user' field exists on 'user'. To use this type in the polymorphic relationship 'user.messages' the relationships schema definition for user should include: + ? "The 'user' type does not implement 'message' and thus cannot be assigned to the 'messages' relationship in 'user'. Make it a descendant of 'message' or use a mixin of the same name." + : `No 'user' field exists on 'user'. To use this type in the polymorphic relationship 'user.messages' the relationships schema definition for user should include: \`\`\` { diff --git a/tests/main/tests/integration/relationships/one-to-one-test.js b/tests/main/tests/integration/relationships/one-to-one-test.js index abf6595b50a..2487e22daa0 100644 --- a/tests/main/tests/integration/relationships/one-to-one-test.js +++ b/tests/main/tests/integration/relationships/one-to-one-test.js @@ -9,6 +9,7 @@ import Model, { attr, belongsTo } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; import { deprecatedTest } from '@ember-data/unpublished-test-infra/test-support/deprecated-test'; import testInDebug from '@ember-data/unpublished-test-infra/test-support/test-in-debug'; +import { DEPRECATE_PROMISE_PROXIES } from '@warp-drive/build-config/deprecations'; module('integration/relationships/one_to_one_test - OneToOne relationships', function (hooks) { setupTest(hooks); @@ -470,7 +471,7 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }); newFriend.bestFriend = stanleysFriend.bestFriend; - const fetchedUser = await stanleysFriend.bestFriend; + const fetchedUser = await stanley.bestFriend; assert.strictEqual( fetchedUser, newFriend, @@ -561,9 +562,14 @@ module('integration/relationships/one_to_one_test - OneToOne relationships', fun }, }); - assert.expectAssertion(function () { - stanley.bestFriend = Promise.resolve(igor); - }, '[object Promise] is not a record instantiated by @ember-data/store'); + assert.expectAssertion( + function () { + stanley.bestFriend = Promise.resolve(igor); + }, + DEPRECATE_PROMISE_PROXIES + ? /You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call./ + : '[object Promise] is not a record instantiated by @ember-data/store' + ); }); deprecatedTest( diff --git a/tests/main/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js b/tests/main/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js index 6357956ff68..8c4c41bb34b 100644 --- a/tests/main/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js +++ b/tests/main/tests/integration/relationships/polymorphic-mixins-belongs-to-test.js @@ -142,7 +142,7 @@ module( user.bestMessage = video; }, DEPRECATE_NON_EXPLICIT_POLYMORPHISM - ? "Assertion Failed: The 'not-message' type does not implement 'message' and thus cannot be assigned to the 'bestMessage' relationship in 'user'. Make it a descendant of 'message' or use a mixin of the same name." + ? "The 'not-message' type does not implement 'message' and thus cannot be assigned to the 'bestMessage' relationship in 'user'. Make it a descendant of 'message' or use a mixin of the same name." : `No 'user' field exists on 'not-message'. To use this type in the polymorphic relationship 'user.bestMessage' the relationships schema definition for not-message should include: \`\`\` diff --git a/tests/main/tests/integration/relationships/promise-many-array-test.js b/tests/main/tests/integration/relationships/promise-many-array-test.js index fa1ddfc72bb..1472d937c2b 100644 --- a/tests/main/tests/integration/relationships/promise-many-array-test.js +++ b/tests/main/tests/integration/relationships/promise-many-array-test.js @@ -89,7 +89,9 @@ module('PromiseManyArray', (hooks) => { owner.register( 'adapter:application', class extends EmberObject { - findRecord() { + findRecord(_store, _schema, id) { + assert.step(`findRecord ${id}`); + assert.strictEqual(id, String(_id + 1), 'findRecord id is correct'); const name = names[_id++]; const data = { type: 'person', @@ -131,6 +133,15 @@ module('PromiseManyArray', (hooks) => { await settled(); + assert.verifySteps([ + 'findRecord 1', + 'findRecord 2', + 'findRecord 3', + 'findRecord 4', + 'findRecord 5', + 'findRecord 6', + ]); + memberIds = group.memberIds; johnRecords = group.johns; assert.strictEqual(memberIds.length, 6, 'memberIds length is correct'); diff --git a/tests/main/tests/unit/model/relationships/has-many-test.js b/tests/main/tests/unit/model/relationships/has-many-test.js index a5b5e67a983..f491717a671 100644 --- a/tests/main/tests/unit/model/relationships/has-many-test.js +++ b/tests/main/tests/unit/model/relationships/has-many-test.js @@ -2112,7 +2112,7 @@ module('unit/model/relationships - 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); + assert.expect(DEPRECATE_ARRAY_LIKE ? 3 : 2); const Tag = Model.extend({ name: attr('string'), @@ -2799,43 +2799,42 @@ module('unit/model/relationships - hasMany', function (hooks) { await settled(); }); - deprecatedTest( - 'checks if passed array only contains instances of Model', - { - id: 'ember-data:deprecate-promise-proxies', - count: IS_DEPRECATE_MANY_ARRAY_DUPLICATES ? 4 : 5, - until: '5.0', - }, - async function (assert) { - const Person = Model.extend(); - const Tag = Model.extend({ - people: hasMany('person', { async: true, inverse: null }), - }); + test('checks if passed array only contains instances of Model', async function (assert) { + class Person extends Model { + @attr name; + } + class Tag extends Model { + @hasMany('person', { async: true, inverse: null }) people; + } - this.owner.register('model:tag', Tag); - this.owner.register('model:person', Person); + this.owner.register('model:tag', Tag); + this.owner.register('model:person', Person); - const store = this.owner.lookup('service:store'); - const adapter = store.adapterFor('application'); + const store = this.owner.lookup('service:store'); + const adapter = store.adapterFor('application'); - adapter.findRecord = function () { - return { - data: { - type: 'person', - id: '1', - }, - }; + adapter.findRecord = function () { + return { + data: { + type: 'person', + id: '1', + }, }; + }; - const tag = store.createRecord('tag'); - const person = store.findRecord('person', '1'); - await person; + const tag = store.createRecord('tag'); + const person = store.findRecord('person', '1'); + await person; - tag.people = [person]; + tag.people = [person]; - assert.expectAssertion(() => { - tag.people = [person, {}]; - }, /All elements of a hasMany relationship must be instances of Model/); - } - ); + assert.expectAssertion(() => { + tag.people = [person, {}]; + }, /All elements of a hasMany relationship must be instances of Model/); + assert.expectDeprecation({ + id: 'ember-data:deprecate-promise-proxies', + count: IS_DEPRECATE_MANY_ARRAY_DUPLICATES ? 4 : 5, + until: '5.0', + }); + }); });