diff --git a/src/js/media/models/attachments.js b/src/js/media/models/attachments.js
index 23510bd949f4c..0b764de042ae6 100644
--- a/src/js/media/models/attachments.js
+++ b/src/js/media/models/attachments.js
@@ -44,7 +44,17 @@ var Attachments = Backbone.Collection.extend(/** @lends wp.media.model.Attachmen
this.props.on( 'change:orderby', this._changeOrderby, this );
this.props.on( 'change:query', this._changeQuery, this );
- this.props.set( _.defaults( options.props || {} ) );
+ options.props = _.defaults( options.props || {} );
+
+ // Normalize the order if it exists.
+ if ( 'string' === typeof options.props.order ) {
+ options.props.order = options.props.order.toUpperCase();
+ if ( 'ASC' !== options.props.order && 'DESC' !== options.props.order ) {
+ options.props.order = 'DESC';
+ }
+ }
+
+ this.props.set( options.props );
if ( options.observe ) {
this.observe( options.observe );
diff --git a/src/js/media/models/query.js b/src/js/media/models/query.js
index b3f62018f5cd4..3c47215c39833 100644
--- a/src/js/media/models/query.js
+++ b/src/js/media/models/query.js
@@ -251,12 +251,6 @@ Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{
// Fill default args.
_.defaults( props, defaults );
- // Normalize the order.
- props.order = props.order.toUpperCase();
- if ( 'DESC' !== props.order && 'ASC' !== props.order ) {
- props.order = defaults.order.toUpperCase();
- }
-
// Ensure we have a valid orderby value.
if ( ! _.contains( orderby.allowed, props.orderby ) ) {
props.orderby = defaults.orderby;
diff --git a/tests/qunit/index.html b/tests/qunit/index.html
index bcd6d8c1c6ddd..ff81b959217c1 100644
--- a/tests/qunit/index.html
+++ b/tests/qunit/index.html
@@ -152,6 +152,7 @@
+
diff --git a/tests/qunit/wp-includes/js/media/test-media-models.js b/tests/qunit/wp-includes/js/media/test-media-models.js
new file mode 100644
index 0000000000000..99642aaecebdd
--- /dev/null
+++ b/tests/qunit/wp-includes/js/media/test-media-models.js
@@ -0,0 +1,248 @@
+/* globals wp */
+/* jshint qunit: true */
+/* eslint-env qunit */
+/* eslint-disable no-magic-numbers */
+
+( function() {
+ 'use strict';
+
+ QUnit.module( 'Media Models - Order Normalization' );
+
+ // Test valid uppercase values
+ QUnit.test( 'Attachments should accept uppercase "ASC" order', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 'ASC'
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'ASC',
+ 'Order should remain ASC when passed as uppercase' );
+ });
+
+ QUnit.test( 'Attachments should accept uppercase "DESC" order', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 'DESC'
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'DESC',
+ 'Order should remain DESC when passed as uppercase' );
+ });
+
+ // Test lowercase normalization
+ QUnit.test( 'Attachments should normalize lowercase "asc" to uppercase', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 'asc'
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'ASC',
+ 'Order should be converted from lowercase asc to uppercase ASC' );
+ });
+
+ QUnit.test( 'Attachments should normalize lowercase "desc" to uppercase', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 'desc'
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'DESC',
+ 'Order should be converted from lowercase desc to uppercase DESC' );
+ });
+
+ // Test mixed case normalization
+ QUnit.test( 'Attachments should normalize mixed case "AsC" to uppercase', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 'AsC'
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'ASC',
+ 'Order should be converted from mixed case AsC to uppercase ASC' );
+ });
+
+ QUnit.test( 'Attachments should normalize mixed case "DeSc" to uppercase', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 'DeSc'
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'DESC',
+ 'Order should be converted from mixed case DeSc to uppercase DESC' );
+ });
+
+ // Test invalid string values
+ QUnit.test( 'Attachments should default invalid string order to "DESC"', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 'invalid'
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'DESC',
+ 'Invalid string order should default to DESC' );
+ });
+
+ QUnit.test( 'Attachments should default empty string order to "DESC"', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: ''
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'DESC',
+ 'Empty string order should default to DESC' );
+ });
+
+ // Test non-string type edge cases
+ QUnit.test( 'Attachments should not process null order value', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: null
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), null,
+ 'Null order should remain null (not processed by normalization)' );
+ });
+
+ QUnit.test( 'Attachments should not process undefined order value', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: undefined
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), undefined,
+ 'Undefined order should remain undefined (not processed by normalization)' );
+ });
+
+ QUnit.test( 'Attachments should not process numeric order value', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 123
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 123,
+ 'Numeric order should remain unchanged (not processed by normalization)' );
+ });
+
+ QUnit.test( 'Attachments should not process boolean true order value', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: true
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), true,
+ 'Boolean true order should remain unchanged (not processed by normalization)' );
+ });
+
+ QUnit.test( 'Attachments should not process boolean false order value', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: false
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), false,
+ 'Boolean false order should remain unchanged (not processed by normalization)' );
+ });
+
+ QUnit.test( 'Attachments should not process object order value', function( assert ) {
+ var orderObj = { value: 'ASC' };
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: orderObj
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), orderObj,
+ 'Object order should remain unchanged (not processed by normalization)' );
+ });
+
+ QUnit.test( 'Attachments should not process array order value', function( assert ) {
+ var orderArray = ['ASC', 'DESC'];
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: orderArray
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), orderArray,
+ 'Array order should remain unchanged (not processed by normalization)' );
+ });
+
+ // Test when no order property is provided
+ QUnit.test( 'Attachments should work when no order property is provided', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ orderby: 'date'
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), undefined,
+ 'Order should be undefined when not provided' );
+ });
+
+ // Test Query model inheritance
+ QUnit.test( 'Query should inherit order normalization from Attachments', function( assert ) {
+ var query = new wp.media.model.Query( [], {
+ props: {
+ order: 'asc',
+ query: true
+ }
+ });
+
+ assert.strictEqual( query.props.get('order'), 'ASC',
+ 'Query model should normalize order through inheritance from Attachments' );
+ assert.ok( query instanceof wp.media.model.Attachments,
+ 'Query should be instance of Attachments' );
+ });
+
+ QUnit.test( 'Query should default invalid order to "DESC"', function( assert ) {
+ var query = new wp.media.model.Query( [], {
+ props: {
+ order: 'random',
+ query: true
+ }
+ });
+
+ assert.strictEqual( query.props.get('order'), 'DESC',
+ 'Query model should default invalid order to DESC' );
+ });
+
+ // Test whitespace handling
+ QUnit.test( 'Attachments should handle order with whitespace', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: ' asc '
+ }
+ });
+
+ assert.notStrictEqual( collection.props.get('order'), 'ASC',
+ 'Order with whitespace should not match ASC exactly' );
+ assert.strictEqual( collection.props.get('order'), 'DESC',
+ 'Order with whitespace should default to DESC as it does not match ASC/DESC after toUpperCase' );
+ });
+
+ // Test unicode characters
+ QUnit.test( 'Attachments should handle order with unicode characters', function( assert ) {
+ var collection = new wp.media.model.Attachments( [], {
+ props: {
+ order: 'asc\u200B' // Zero-width space
+ }
+ });
+
+ assert.strictEqual( collection.props.get('order'), 'DESC',
+ 'Order with unicode characters should default to DESC' );
+ });
+
+})();