Repository: fabricjs/fabric.js
Pull request #9474 (2023-11-03 04:13:08)

chore(): Reorder defaults for fabric object + patch `setRelativeXY`
## Motivation
Original idea was move fabric object constructor tests into jest. This revealed that the test was testing for a property that FabricObject doesn't even have and led to moving properties for that into InteractiveFabricObject, which did have them. Also created brief constructor test for InteractiveFabricObject for property in that class. 

## Description

Move InteractiveFabricObject defaults to its proper file so as not to have defaults not typed to FabricObject assigned. Re-arrange the remaining properties by class name and order in the class.

## Changes
- Move InteractiveObject defaults to its file
- Reorder defaults for fabric object by the class name and how the class has the properties ordered
- Move object constructor qunit tests to jest
- Move FX_DURATION to straighten since that's the only thing using it and it's in the parking lot
- Move `colorProperties` to static

gloriousjob: I was curious what migrating some of the tests from qunit to jest might reveal because of my previous PR. Let me know if this makes sense or is a bit much (i.e., you don't want tests moved over).
asturur: This is not wrong imho, but since i don't want to scroll the page up and down and check properties one by one, before adding this i will add a test in master that checks that the end result will be the same.

In general there is an issue with reordering things:
- useless diffs
- tainted history ( when did we change this file last time? 3 days ago? oh no it was just an alphabetical sort... )

so in general i m against diffs in pr that are done for example to reorder imports.
gloriousjob: > This is not wrong imho, but since i don't want to scroll the page up and down and check properties one by one, before adding this i will add a test in master that checks that the end result will be the same.
> 
> In general there is an issue with reordering things:
> 
> * useless diffs
> * tainted history ( when did we change this file last time? 3 days ago? oh no it was just an alphabetical sort... )
> 
> so in general i m against diffs in pr that are done for example to reorder imports.

I understand not wanting to reorder so you don't add history you don't want (and you have to go somewhere else anyway to find the default so may not matter that the order is different). What do you think about moving the defaults that aren't in fabric object? Also about migrating tests? The migration revealed that selectable isn't a part of fabric object but the test was passing because the default is being crammed into there, even enough it's a property of a subclass. I can redo the branch and keep the original order but I think the owndefaults should represent what they are.
ShaMan123: migrating tests is welcome
asturur: i m adding the test in master so we can merge this if the test pass
asturur: @gloriousjob there is some nasty merge conflict. can you fix it?
asturur: ok maybe i solved the conflict
asturur: I added the test and merged master in, i let the discussion on the best ordering to you guys.
Choose one and don't touch it for long time after.
gloriousjob: > I added the test and merged master in, i let the discussion on the best ordering to you guys.
> Choose one and don't touch it for long time after.

Thanks! I'll work with @ShaMan123 for now. Not saying we'll get to the perfect place but hopefully better.
ShaMan123: Can you rebase/update from master?
ShaMan123: Can you add a changelog entry? Then I can merge.
I changed computer and I am not sure how to push to the PR anymore... previously I used GitHub desktop for that.
gloriousjob: Not sure if I did it right but just checked in an additional line. I tried to make it match the lines around it.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c68cb4a33..37a28103d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ## [next]
 
+- refactor(): Separate defaults for base fabric object vs interactive object. Also some moving around of variables [#9474](https://github.com/fabricjs/fabric.js/pull/9474)
 - refactor(): Change how LayoutManager handles restoring groups [#9522](https://github.com/fabricjs/fabric.js/pull/9522)
 - fix(BaseConfiguration): set `devicePixelRatio` from window [#9470](https://github.com/fabricjs/fabric.js/pull/9470)
 - fix(): bubble dirty flag for group only when true [#9540](https://github.com/fabricjs/fabric.js/pull/9540)
diff --git a/src/parkinglot/straighten.ts b/src/parkinglot/straighten.ts
index 352fe68eb..1795179f9 100644
--- a/src/parkinglot/straighten.ts
+++ b/src/parkinglot/straighten.ts
@@ -5,6 +5,13 @@ import { TDegree } from '../typedefs';
 import { animate } from '../util/animation/animate';
 
 Object.assign(FabricObject.prototype, {
+  /**
+   * Animation duration (in ms) for fx* methods
+   * @type Number
+   * @default
+   */
+  FX_DURATION: 500,
+
   /**
    * @private
    * @return {Number} angle value
diff --git a/src/shapes/Object/AnimatableObject.ts b/src/shapes/Object/AnimatableObject.ts
index 0a2bb58ba..08456f105 100644
--- a/src/shapes/Object/AnimatableObject.ts
+++ b/src/shapes/Object/AnimatableObject.ts
@@ -17,7 +17,7 @@ export abstract class AnimatableObject<
    * List of properties to consider for animating colors.
    * @type String[]
    */
-  declare colorProperties: string[];
+  static colorProperties: string[] = ['fill', 'stroke', 'backgroundColor'];
 
   /**
    * Animates object's properties
@@ -53,7 +53,9 @@ export abstract class AnimatableObject<
     options: Partial<AnimationOptions<T>> = {}
   ): TAnimation<T> {
     const path = key.split('.');
-    const propIsColor = this.colorProperties.includes(path[path.length - 1]);
+    const propIsColor = (
+      this.constructor as typeof AnimatableObject
+    ).colorProperties.includes(path[path.length - 1]);
     const { easing, duration, abort, startValue, onChange, onComplete } =
       options;
     const animationOptions = {
diff --git a/src/shapes/Object/InteractiveObject.spec.ts b/src/shapes/Object/InteractiveObject.spec.ts
index 3d17ce0d9..cd1e56b5d 100644
--- a/src/shapes/Object/InteractiveObject.spec.ts
+++ b/src/shapes/Object/InteractiveObject.spec.ts
@@ -2,9 +2,14 @@ import { radiansToDegrees } from '../../util';
 import { Group } from '../Group';
 import { Canvas } from '../../canvas/Canvas';
 import { FabricObject } from './FabricObject';
-import type { TOCoord } from './InteractiveObject';
+import { InteractiveFabricObject, type TOCoord } from './InteractiveObject';
 
-describe('FabricObject', () => {
+describe('InteractiveObject', () => {
+  it('tests constructor & properties', () => {
+    const obj = new InteractiveFabricObject();
+    expect(obj instanceof InteractiveFabricObject).toBe(true);
+    expect(obj.selectable).toBe(true);
+  });
   it('Interactive + BaseObject default values', () => {
     const { controls, ...defaults } = FabricObject.getDefaults();
     expect(defaults).toMatchSnapshot();
diff --git a/src/shapes/Object/InteractiveObject.ts b/src/shapes/Object/InteractiveObject.ts
index 9652d44f1..80080f64b 100644
--- a/src/shapes/Object/InteractiveObject.ts
+++ b/src/shapes/Object/InteractiveObject.ts
@@ -18,6 +18,7 @@ import type { ControlRenderingStyleOverride } from '../../controls/controlRender
 import type { FabricObjectProps } from './types/FabricObjectProps';
 import type { TFabricObjectProps, SerializedObjectProps } from './types';
 import { createObjectDefaultControls } from '../../controls/commonControls';
+import { interactiveObjectDefaultValues } from './defaultValues';
 
 export type TOCoord = Point & {
   corner: TCornerPoint;
@@ -38,8 +39,6 @@ export type TStyleOverride = ControlRenderingStyleOverride &
     }
   >;
 
-const interactiveDefaults = {};
-
 export class InteractiveFabricObject<
     Props extends TFabricObjectProps = Partial<FabricObjectProps>,
     SProps extends SerializedObjectProps = SerializedObjectProps,
@@ -49,7 +48,6 @@ export class InteractiveFabricObject<
   implements FabricObjectProps
 {
   declare noScaleCache: boolean;
-  declare centeredScaling: boolean;
 
   declare snapAngle?: TDegree;
   declare snapThreshold?: TDegree;
@@ -136,7 +134,7 @@ export class InteractiveFabricObject<
 
   declare canvas?: Canvas;
 
-  static ownDefaults: Record<string, any> = interactiveDefaults;
+  static ownDefaults: Record<string, any> = interactiveObjectDefaultValues;
 
   static getDefaults(): Record<string, any> {
     return {
diff --git a/src/shapes/Object/Object.spec.ts b/src/shapes/Object/Object.spec.ts
index d209338a6..66abfc858 100644
--- a/src/shapes/Object/Object.spec.ts
+++ b/src/shapes/Object/Object.spec.ts
@@ -4,6 +4,19 @@ import { FabricObject } from './Object';
 import { Group } from '../Group';
 
 describe('Object', () => {
+  it('tests constructor & properties', () => {
+    expect(typeof FabricObject).toBe('function');
+    const cObj = new FabricObject();
+    expect(cObj).toBeDefined();
+    expect(cObj instanceof FabricObject).toBe(true);
+    expect(cObj.constructor).toBe(FabricObject);
+
+    expect((cObj.constructor as typeof FabricObject).type).toBe('FabricObject');
+    expect(cObj.includeDefaultValues).toBe(true);
+
+    //TODO: Add message 'object caching default value'
+    expect(cObj.objectCaching).toBe(true);
+  });
   it('rotate with centered rotation', () => {
     const fObj = new FabricObject({
       centeredRotation: true,
diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts
index e3b7d6e42..71e57418d 100644
--- a/src/shapes/Object/Object.ts
+++ b/src/shapes/Object/Object.ts
@@ -136,6 +136,7 @@ export class FabricObject<
   declare inverted: boolean;
   declare absolutePositioned: boolean;
   declare centeredRotation: boolean;
+  declare centeredScaling: boolean;
 
   /**
    * This list of properties is used to check if the state of an object is changed.
diff --git a/src/shapes/Object/ObjectGeometry.ts b/src/shapes/Object/ObjectGeometry.ts
index cbf754b95..6bace4080 100644
--- a/src/shapes/Object/ObjectGeometry.ts
+++ b/src/shapes/Object/ObjectGeometry.ts
@@ -170,12 +170,12 @@ export class ObjectGeometry<EventSpec extends ObjectEvents = ObjectEvents>
    * @param {TOriginX} [originX] Horizontal origin: 'left', 'center' or 'right'
    * @param {TOriginY} [originY] Vertical origin: 'top', 'center' or 'bottom'
    */
-  setRelativeXY(point: Point, originX?: TOriginX, originY?: TOriginY) {
-    this.setPositionByOrigin(
-      point,
-      originX || this.originX,
-      originY || this.originY
-    );
+  setRelativeXY(
+    point: Point,
+    originX: TOriginX = this.originX,
+    originY: TOriginY = this.originY
+  ) {
+    this.setPositionByOrigin(point, originX, originY);
   }
 
   /**
diff --git a/src/shapes/Object/__snapshots__/InteractiveObject.spec.ts.snap b/src/shapes/Object/__snapshots__/InteractiveObject.spec.ts.snap
index af89132f9..570d0f399 100644
--- a/src/shapes/Object/__snapshots__/InteractiveObject.spec.ts.snap
+++ b/src/shapes/Object/__snapshots__/InteractiveObject.spec.ts.snap
@@ -1,8 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`FabricObject Interactive + BaseObject default values 1`] = `
+exports[`InteractiveObject Interactive + BaseObject default values 1`] = `
 {
-  "FX_DURATION": 500,
   "absolutePositioned": false,
   "activeOn": "down",
   "angle": 0,
@@ -14,11 +13,6 @@ exports[`FabricObject Interactive + BaseObject default values 1`] = `
   "centeredRotation": true,
   "centeredScaling": false,
   "clipPath": undefined,
-  "colorProperties": [
-    "fill",
-    "stroke",
-    "backgroundColor",
-  ],
   "cornerColor": "rgb(178,204,255)",
   "cornerDashArray": null,
   "cornerSize": 13,
diff --git a/src/shapes/Object/defaultValues.ts b/src/shapes/Object/defaultValues.ts
index 056d31af1..9f22ad184 100644
--- a/src/shapes/Object/defaultValues.ts
+++ b/src/shapes/Object/defaultValues.ts
@@ -36,57 +36,51 @@ export const cacheProperties = [
 ];
 
 export const fabricObjectDefaultValues = {
-  originX: LEFT,
-  originY: TOP,
+  // see composeMatrix() to see order of transforms. First defaults listed based on this
   top: 0,
   left: 0,
   width: 0,
   height: 0,
-  scaleX: 1,
-  scaleY: 1,
+  angle: 0,
   flipX: false,
   flipY: false,
-  opacity: 1,
-  angle: 0,
+  scaleX: 1,
+  scaleY: 1,
+  minScaleLimit: 0,
   skewX: 0,
   skewY: 0,
-  cornerSize: 13,
-  touchCornerSize: 24,
-  transparentCorners: true,
-  hoverCursor: null,
-  moveCursor: null,
+  originX: LEFT,
+  originY: TOP,
+  strokeWidth: 1,
+  strokeUniform: false,
   padding: 0,
-  borderColor: 'rgb(178,204,255)',
-  borderDashArray: null,
-  cornerColor: 'rgb(178,204,255)',
-  cornerStrokeColor: '',
-  cornerStyle: 'rect',
-  cornerDashArray: null,
-  centeredScaling: false,
-  centeredRotation: true,
+  opacity: 1,
+  paintFirst: 'fill',
   fill: 'rgb(0,0,0)',
   fillRule: 'nonzero',
-  globalCompositeOperation: 'source-over',
-  backgroundColor: '',
-  selectionBackgroundColor: '',
   stroke: null,
-  strokeWidth: 1,
   strokeDashArray: null,
   strokeDashOffset: 0,
   strokeLineCap: 'butt',
   strokeLineJoin: 'miter',
   strokeMiterLimit: 4,
+  globalCompositeOperation: 'source-over',
+  backgroundColor: '',
   shadow: null,
-  borderOpacityWhenMoving: 0.4,
-  borderScaleFactor: 1,
-  minScaleLimit: 0,
-  selectable: true,
-  evented: true,
   visible: true,
-  hasControls: true,
-  hasBorders: true,
-  perPixelTargetFind: false,
   includeDefaultValues: true,
+  excludeFromExport: false,
+  objectCaching: true,
+  clipPath: undefined,
+  inverted: false,
+  absolutePositioned: false,
+  centeredRotation: true,
+  centeredScaling: false,
+  dirty: true,
+} as const;
+
+export const interactiveObjectDefaultValues = {
+  noScaleCache: true,
   lockMovementX: false,
   lockMovementY: false,
   lockRotation: false,
@@ -95,16 +89,24 @@ export const fabricObjectDefaultValues = {
   lockSkewingX: false,
   lockSkewingY: false,
   lockScalingFlip: false,
-  excludeFromExport: false,
-  objectCaching: true,
-  noScaleCache: true,
-  strokeUniform: false,
-  dirty: true,
-  paintFirst: 'fill',
+  cornerSize: 13,
+  touchCornerSize: 24,
+  transparentCorners: true,
+  cornerColor: 'rgb(178,204,255)',
+  cornerStrokeColor: '',
+  cornerStyle: 'rect',
+  cornerDashArray: null,
+  hasControls: true,
+  borderColor: 'rgb(178,204,255)',
+  borderDashArray: null,
+  borderOpacityWhenMoving: 0.4,
+  borderScaleFactor: 1,
+  hasBorders: true,
+  selectionBackgroundColor: '',
+  selectable: true,
+  evented: true,
+  perPixelTargetFind: false,
   activeOn: 'down',
-  colorProperties: ['fill', 'stroke', 'backgroundColor'],
-  clipPath: undefined,
-  inverted: false,
-  absolutePositioned: false,
-  FX_DURATION: 500,
-} as const;
+  hoverCursor: null,
+  moveCursor: null,
+};
diff --git a/src/shapes/Object/types/BaseProps.ts b/src/shapes/Object/types/BaseProps.ts
index 93a7f12ca..e8bb44ea4 100644
--- a/src/shapes/Object/types/BaseProps.ts
+++ b/src/shapes/Object/types/BaseProps.ts
@@ -2,18 +2,22 @@ import type { TDegree, TOriginX, TOriginY } from '../../../typedefs';
 
 export interface BaseProps {
   /**
-   * Top position of an object. Note that by default it's relative to object top. You can change this by setting originY={top/center/bottom}
+   * Left position of an object.
+   * Note that by default it's relative to object left.
+   * You can change this by setting {@link originX}
    * @type Number
    * @default 0
    */
-  top: number;
+  left: number;
 
   /**
-   * Left position of an object. Note that by default it's relative to object left. You can change this by setting originX={left/center/right}
+   * Top position of an object.
+   * Note that by default it's relative to object top.
+   * You can change this by setting {@link originY}
    * @type Number
    * @default 0
    */
-  left: number;
+  top: number;
 
   /**
    * Object width
@@ -29,6 +33,29 @@ export interface BaseProps {
    */
   height: number;
 
+  /**
+   * Horizontal origin of transformation of an object (`left`, `center`, `right`  or `[0, 1]`)
+   * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups
+   * @type String
+   * @default 'left'
+   */
+  originX: TOriginX;
+
+  /**
+   * Vertical origin of transformation of an object (`top`, `center`, `bottom` or `[0, 1]`)
+   * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups
+   * @type String
+   * @default 'top'
+   */
+  originY: TOriginY;
+
+  /**
+   * Angle of rotation of an object (in degrees)
+   * @type Number
+   * @default 0
+   */
+  angle: TDegree;
+
   /**
    * When true, an object is rendered as flipped horizontally
    * @type Boolean
@@ -62,35 +89,12 @@ export interface BaseProps {
    * @type Number
    * @default 0
    */
-  skewX: number;
+  skewX: TDegree;
 
   /**
    * Angle of skew on y axes of an object (in degrees)
    * @type Number
    * @default 0
    */
-  skewY: number;
-
-  /**
-   * Horizontal origin of transformation of an object (one of "left", "right", "center")
-   * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups
-   * @type String
-   * @default 'left'
-   */
-  originX: TOriginX;
-
-  /**
-   * Vertical origin of transformation of an object (one of "top", "bottom", "center")
-   * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups
-   * @type String
-   * @default 'top'
-   */
-  originY: TOriginY;
-
-  /**
-   * Angle of rotation of an object (in degrees)
-   * @type Number
-   * @default 0
-   */
-  angle: TDegree;
+  skewY: TDegree;
 }
diff --git a/src/shapes/Object/types/FabricObjectProps.ts b/src/shapes/Object/types/FabricObjectProps.ts
index de3ef4063..f242d5437 100644
--- a/src/shapes/Object/types/FabricObjectProps.ts
+++ b/src/shapes/Object/types/FabricObjectProps.ts
@@ -1,4 +1,3 @@
-import type { TDegree } from '../../../typedefs';
 import type { BorderProps } from './BorderProps';
 import type { ControlProps } from './ControlProps';
 import type { LockInteractionProps } from './LockInteractionProps';
@@ -20,39 +19,6 @@ export interface FabricObjectProps
    */
   noScaleCache?: boolean;
 
-  /**
-   * When true, this object will use center point as the origin of transformation
-   * when being scaled via the controls.
-   * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
-   * @since 1.3.4
-   * @type Boolean
-   * @default
-   */
-  centeredScaling: boolean;
-
-  /**
-   * When true, this object will use center point as the origin of transformation
-   * when being rotated via the controls.
-   * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
-   * @since 1.3.4
-   * @type Boolean
-   * @default
-   */
-  centeredRotation: boolean;
-
-  /**
-   * The angle that an object will lock to while rotating.
-   * @type [TDegree]
-   */
-  snapAngle?: TDegree;
-
-  /**
-   * The angle difference from the current snapped angle in which snapping should occur.
-   * When undefined, the snapThreshold will default to the snapAngle.
-   * @type [TDegree]
-   */
-  snapThreshold?: TDegree;
-
   /**
    * Default cursor value used when hovering over this object on canvas
    * @type CSSStyleDeclaration['cursor'] | null
diff --git a/src/shapes/Object/types/ObjectProps.ts b/src/shapes/Object/types/ObjectProps.ts
index 4cf7bc48a..62fd449d0 100644
--- a/src/shapes/Object/types/ObjectProps.ts
+++ b/src/shapes/Object/types/ObjectProps.ts
@@ -3,12 +3,16 @@ import type { Canvas } from '../../../canvas/Canvas';
 import type { StaticCanvas } from '../../../canvas/StaticCanvas';
 import type { TFiller } from '../../../typedefs';
 import type { FabricObject } from '../Object';
+import type { ObjectTransformActionProps } from './ObjectTransformProps';
 import type {
   ClipPathProps,
   SerializedObjectProps,
 } from './SerializedObjectProps';
 
-export interface ObjectProps extends SerializedObjectProps, ClipPathProps {
+export interface ObjectProps
+  extends SerializedObjectProps,
+    ClipPathProps,
+    ObjectTransformActionProps {
   clipPath?: FabricObject;
   fill: TFiller | string | null;
   stroke: TFiller | string | null;
@@ -46,14 +50,4 @@ export interface ObjectProps extends SerializedObjectProps, ClipPathProps {
    * @default
    */
   excludeFromExport: boolean;
-
-  /**
-   * When `true` the object will rotate on its center.
-   * When `false` will rotate around the origin point defined by originX and originY.
-   * The value of this property is IGNORED during a transform if the canvas has already
-   * centeredRotation set to `true`
-   * The object method `rotate` will always consider this property and never the canva's one.
-   * @default true
-   */
-  centeredRotation: boolean;
 }
diff --git a/src/shapes/Object/types/ObjectTransformProps.ts b/src/shapes/Object/types/ObjectTransformProps.ts
new file mode 100644
index 000000000..a0bebccde
--- /dev/null
+++ b/src/shapes/Object/types/ObjectTransformProps.ts
@@ -0,0 +1,37 @@
+import type { TDegree } from '../../../typedefs';
+
+export interface ObjectTransformActionProps {
+  /**
+   * The angle that an object will lock to while rotating.
+   * @type [TDegree]
+   */
+  snapAngle?: TDegree;
+
+  /**
+   * The angle difference from the current snapped angle in which snapping should occur.
+   * When undefined, the snapThreshold will default to the snapAngle.
+   * @type [TDegree]
+   */
+  snapThreshold?: TDegree;
+
+  /**
+   * When `true` the object will rotate on its center.
+   * When `false` will rotate around the origin point defined by originX and originY.
+   * The value of this property is IGNORED during a transform if the canvas has already
+   * centeredRotation set to `true`
+   * The object method `rotate` will always consider this property and never the canvas's one.
+   * @since 1.3.4
+   * @type Boolean
+   * @default
+   */
+  centeredRotation: boolean;
+
+  /**
+   * When true, this object will use center point as the origin of transformation
+   * when being scaled via the controls.
+   * @since 1.3.4
+   * @type Boolean
+   * @default
+   */
+  centeredScaling: boolean;
+}


Test patch:
diff --git a/test/unit/animation.js b/test/unit/animation.js
index 56891f519..46b1275bb 100644
--- a/test/unit/animation.js
+++ b/test/unit/animation.js
@@ -313,7 +313,7 @@
   QUnit.test('animate with color', function(assert) {
     var done = assert.async(),
         object = new fabric.Object(),
-        properties = fabric.Object.getDefaults().colorProperties;
+        properties = fabric.Object.colorProperties;
 
     properties.forEach(function (prop, index) {
       object.set(prop, 'red');
diff --git a/test/unit/object.js b/test/unit/object.js
index f9ec160a5..9f8350dd6 100644
--- a/test/unit/object.js
+++ b/test/unit/object.js
@@ -18,22 +18,6 @@
     }
   });
 
-  QUnit.test('constructor & properties', function(assert) {
-    assert.ok(typeof fabric.Object === 'function');
-
-    var cObj = new fabric.Object();
-
-    assert.ok(cObj);
-    assert.ok(cObj instanceof fabric.FabricObject);
-    assert.ok(cObj.constructor === fabric.Object);
-
-    assert.equal(cObj.constructor.type, 'FabricObject');
-    assert.equal(cObj.includeDefaultValues, true);
-    assert.equal(cObj.selectable, true);
-
-    assert.equal(cObj.objectCaching, !isNode(), 'object caching default value');
-  });
-
   QUnit.test('get', function(assert) {
     var cObj = new fabric.Object({
       left: 11,