From 9ca04cf636c990712b0db1c8fcce3ce8c6038e93 Mon Sep 17 00:00:00 2001 From: Julian Descottes <jdescottes@mozilla.com> Date: Mon, 5 Sep 2022 13:00:15 +0000 Subject: [PATCH] Bug 1789052 - [devtools] Fix storage inspector sorting logic. r=nchevobbe, a=RyanVM Two issues fixed: - when sorting on one column, other column content should be re-arranged - natural order sort was not symmetrical for dates and led to inconsistent sorting Differential Revision: https://phabricator.services.mozilla.com/D156420 --- devtools/client/shared/widgets/TableWidget.js | 9 ++- devtools/client/storage/test/browser.ini | 2 + .../test/browser_storage_cookies_sort.js | 64 +++++++++++++++++++ .../storage/test/storage-cookies-sort.html | 26 ++++++++ devtools/shared/natural-sort.js | 8 +-- .../tests/xpcshell/test_natural-sort.js | 18 ++++++ 6 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 devtools/client/storage/test/browser_storage_cookies_sort.js create mode 100644 devtools/client/storage/test/storage-cookies-sort.html diff --git a/devtools/client/shared/widgets/TableWidget.js b/devtools/client/shared/widgets/TableWidget.js index f2f9480803d68..c0c0558763bdb 100644 --- a/devtools/client/shared/widgets/TableWidget.js +++ b/devtools/client/shared/widgets/TableWidget.js @@ -999,9 +999,16 @@ TableWidget.prototype = { return; } + // First sort the column to "sort by" explicitly. const sortedItems = this.columns.get(column).sort([...this.items.values()]); + + // Then, sort all the other columns (id !== column) only based on the + // sortedItems provided by the first sort. + // Each column keeps track of the fact that it is the "sort by" column or + // not, so this will not shuffle the items and will just make sure each + // column displays the correct value. for (const [id, col] of this.columns) { - if (id === col) { + if (id !== column) { col.sort(sortedItems); } } diff --git a/devtools/client/storage/test/browser.ini b/devtools/client/storage/test/browser.ini index 186d39e7b740e..a92df77982734 100644 --- a/devtools/client/storage/test/browser.ini +++ b/devtools/client/storage/test/browser.ini @@ -11,6 +11,7 @@ support-files = storage-complex-values.html storage-cookies.html storage-cookies-samesite.html + storage-cookies-sort.html storage-dfpi.html storage-empty-objectstores.html storage-file-url.html @@ -62,6 +63,7 @@ skip-if = os == "linux" && debug && fission && socketprocess_networking # high frequency intermittent [browser_storage_cookies_samesite.js] skip-if = true # Bug 1448484 - sameSite1 is "Unset" - Got undefined, expected Unset +[browser_storage_cookies_sort.js] [browser_storage_cookies_tab_navigation.js] [browser_storage_delete.js] [browser_storage_delete_all.js] diff --git a/devtools/client/storage/test/browser_storage_cookies_sort.js b/devtools/client/storage/test/browser_storage_cookies_sort.js new file mode 100644 index 0000000000000..bb9bdbde25b3c --- /dev/null +++ b/devtools/client/storage/test/browser_storage_cookies_sort.js @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Test column sorting works and sorts dates correctly (including "session" +// cookies). + +"use strict"; + +add_task(async function() { + const TEST_URL = MAIN_DOMAIN + "storage-cookies-sort.html"; + await openTabAndSetupStorage(TEST_URL); + showAllColumns(true); + + info("Sort on the expires column, ascending order"); + clickColumnHeader("expires"); + + // Note that here we only specify `test_session` for `test_session1` and + // `test_session2`. Since we sort on the "expires" column, there is no point + // in asserting the order between those 2 items. + checkCells([ + "test_session", + "test_session", + "test_hour", + "test_day", + "test_year", + ]); + + info("Sort on the expires column, descending order"); + clickColumnHeader("expires"); + + // Again, only assert `test_session` for `test_session1` and `test_session2`. + checkCells([ + "test_year", + "test_day", + "test_hour", + "test_session", + "test_session", + ]); + + info("Sort on the name column, ascending order"); + clickColumnHeader("name"); + checkCells([ + "test_day", + "test_hour", + "test_session1", + "test_session2", + "test_year", + ]); +}); + +function checkCells(expected) { + const cells = [ + ...gPanelWindow.document.querySelectorAll("#name .table-widget-cell"), + ]; + cells.forEach(function(cell, i, arr) { + // We use startsWith in order to avoid asserting the relative order of + // "session" cookies when sorting on the "expires" column. + ok( + cell.value.startsWith(expected[i]), + `Cell value starts with "${expected[i]}".` + ); + }); +} diff --git a/devtools/client/storage/test/storage-cookies-sort.html b/devtools/client/storage/test/storage-cookies-sort.html new file mode 100644 index 0000000000000..fb590d5cb2964 --- /dev/null +++ b/devtools/client/storage/test/storage-cookies-sort.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<html> + <!-- + Bug 970517 - Storage inspector front end - tests + --> + <head> + <meta charset="utf-8"> + <title>Storage inspector cookie test</title> + </head> + <body> + <script type="application/javascript"> + "use strict"; + const ONE_HOUR = 60 * 60 * 1000; + const ONE_DAY = 24 * ONE_HOUR; + const expiresOneHour = new Date(Date.now() + 1 * ONE_HOUR).toUTCString(); + const expiresOneDay = new Date(Date.now() + 1 * ONE_DAY).toUTCString(); + const expiresOneYear = new Date(Date.now() + 365 * ONE_DAY).toUTCString(); + + document.cookie = "test_hour=hour;expires=" + expiresOneHour; + document.cookie = "test_session1=session1"; + document.cookie = "test_day=day;expires=" + expiresOneDay; + document.cookie = "test_session2=session2"; + document.cookie = "test_year=year;expires=" + expiresOneYear; + </script> + </body> +</html> diff --git a/devtools/shared/natural-sort.js b/devtools/shared/natural-sort.js index 5cb41b6dc7483..198980d9cd5a7 100644 --- a/devtools/shared/natural-sort.js +++ b/devtools/shared/natural-sort.js @@ -15,7 +15,6 @@ "use strict"; const tokenizeNumbersRx = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g; -const dateRx = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/; const hexRx = /^0x[0-9a-f]+$/i; const startsWithNullRx = /^\0/; const endsWithNullRx = /\0$/; @@ -74,9 +73,7 @@ function naturalSort(a = "", b = "", insensitive = false) { const aHexOrDate = parseInt(a.match(hexRx), 16) || (aChunks.length !== 1 && Date.parse(a)); const bHexOrDate = - parseInt(b.match(hexRx), 16) || - (aHexOrDate && b.match(dateRx) && Date.parse(b)) || - null; + parseInt(b.match(hexRx), 16) || (bChunks.length !== 1 && Date.parse(b)); if ( (aHexOrDate || bHexOrDate) && @@ -92,12 +89,13 @@ function naturalSort(a = "", b = "", insensitive = false) { } // Try and sort Hex codes or Dates. - if (bHexOrDate) { + if (aHexOrDate && bHexOrDate) { if (aHexOrDate < bHexOrDate) { return -1; } else if (aHexOrDate > bHexOrDate) { return 1; } + return 0; } // Natural sorting through split numeric strings and default strings diff --git a/devtools/shared/tests/xpcshell/test_natural-sort.js b/devtools/shared/tests/xpcshell/test_natural-sort.js index 95d52c61aadc0..3e04c947ac007 100644 --- a/devtools/shared/tests/xpcshell/test_natural-sort.js +++ b/devtools/shared/tests/xpcshell/test_natural-sort.js @@ -178,6 +178,24 @@ function run_test() { ], `"${sessionString}" amongst date strings` ); + runTest( + [ + "Wed, 04 Sep 2024 09:11:44 GMT", + sessionString, + "Tue, 06 Sep 2022 09:11:44 GMT", + sessionString, + "Mon, 05 Sep 2022 09:12:41 GMT", + ], + [ + sessionString, + sessionString, + "Mon, 05 Sep 2022 09:12:41 GMT", + "Tue, 06 Sep 2022 09:11:44 GMT", + "Wed, 04 Sep 2024 09:11:44 GMT", + ], + `"${sessionString}" amongst date strings (complex)` + ); + runTest( [ "Madras", -- GitLab