From bd50f67fba11751162a1f6aa64cf44473dc48e71 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Mon, 8 Jan 2024 17:22:26 +1300 Subject: [PATCH 1/9] Add a couple of unit tests to test PageTree --- WordPress/WordPressTest/PagesListTests.swift | 84 +++++++++++++++++++- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/WordPress/WordPressTest/PagesListTests.swift b/WordPress/WordPressTest/PagesListTests.swift index a3a9e124d6b3..fa29567fce17 100644 --- a/WordPress/WordPressTest/PagesListTests.swift +++ b/WordPress/WordPressTest/PagesListTests.swift @@ -22,6 +22,11 @@ class PagesListTests: CoreDataTestCase { try makeAssertions(pages: pages) } + func testOneNestedListInReversedOrder() throws { + let pages = parentPage(childrenCount: 17, additionalLevels: 7).reversed() + try makeAssertions(pages: Array(pages)) + } + func testManyNestedLists() throws { let groups = [ parentPage(childrenCount: 5), @@ -108,6 +113,32 @@ class PagesListTests: CoreDataTestCase { try makeAssertions(pages: pages) } + func testDistantChildAndParentPages() throws { + let child = PageBuilder(mainContext).build() + child.postID = NSNumber(value: randomID.next()) + child.parentID = NSNumber(value: randomID.next()) + + let parent = PageBuilder(mainContext).build() + parent.postID = child.parentID + parent.parentID = 0 + + let manyPages = parentPage(childrenCount: 17, additionalLevels: 7) + + // Test 1: place the child page at the begining and the parent page at the end. + var sorted = try PageTree.hierarchyList(of: [child] + manyPages + [parent]) + XCTAssertEqual(parent.hierarchyIndex, 0) + XCTAssertEqual(child.hierarchyIndex, 1) + // The child page should follow the parent page in the sorted list + try XCTAssertEqual(XCTUnwrap(sorted.firstIndex(of: parent)) + 1, XCTUnwrap(sorted.firstIndex(of: child))) + + // Test 2: place the child page at the end and the parent page at the begining. + sorted = try PageTree.hierarchyList(of: [parent] + manyPages + [child]) + XCTAssertEqual(parent.hierarchyIndex, 0) + XCTAssertEqual(child.hierarchyIndex, 1) + // The child page should follow the parent page in the sorted list + try XCTAssertEqual(XCTUnwrap(sorted.firstIndex(of: parent)) + 1, XCTUnwrap(sorted.firstIndex(of: child))) + } + func testHierachyListRepresentationRoundtrip() throws { let roundtrip: (String) throws -> Void = { string in let pages = try Array(hierarchyListRepresentation: string, context: self.mainContext) @@ -175,10 +206,27 @@ class PagesListTests: CoreDataTestCase { _ = pages.sorted { ($0.postID?.int64Value ?? 0) < ($1.postID?.int64Value ?? 0) } NSLog("Array.sort took \(String(format: "%.3f", (CFAbsoluteTimeGetCurrent() - start) * 1000)) millisecond to process \(pages.count) pages") - let originalIDs = original.map { $0.postID! } - let newIDs = new.map { $0.postID! } - let diff = originalIDs.difference(from: newIDs).inferringMoves() - XCTAssertTrue(diff.count == 0, "Unexpected diff: \(diff)", file: file, line: line) + // Compare the two implementions to make sure their results are similar. The pages don'n't need to be in the exact same order, + // but each hierachy level should contain the same child pages in it. + + let originalList = HierachyList(pages: original) + let newList = HierachyList(pages: new) + + // They have the same hierachy level. + XCTAssertEqual(originalList.numberOfLevels, newList.numberOfLevels) + + // For each hierachy level, the same child pages are present in both results, without the need of being in the same order. + for level in 1...(originalList.numberOfLevels) { + let pagesAtLevelOriginal = originalList.pages(atLevel: level) + let pagesAtLevelNew = newList.pages(atLevel: level) + XCTAssertEqual(Set(pagesAtLevelOriginal.keys), Set(pagesAtLevelNew.keys), "The parent page ids in each level should be the same") + + for parentPageID in pagesAtLevelOriginal.keys { + let childrenPageIDsOriginal = try XCTUnwrap(pagesAtLevelOriginal[parentPageID]).map { $0.postID } + let childrenPageIDsNew = try XCTUnwrap(pagesAtLevelNew[parentPageID]).map { $0.postID } + XCTAssertEqual(Set(childrenPageIDsOriginal), Set(childrenPageIDsNew), "The children page ids in each level should be the same") + } + } } } @@ -252,3 +300,31 @@ private extension Array where Element == Page { self = pages } } + +private struct HierachyList { + let pages: [Page] + + var numberOfLevels: Int { + pages.map { $0.hierarchyIndex }.max()! + 1 + } + + func pages(atLevel level: Int) -> [NSNumber: [Page]] { + var result = [NSNumber: [Page]]() + for page in pages { + guard page.hierarchyIndex + 1 == level else { + continue + } + + let parentID = page.parentID ?? 0 + result[parentID, default: []].append(page) + } + return result + } + + func print() { + for page in pages { + Swift.print(String(repeating: " ", count: page.hierarchyIndex * 2), terminator: "|- ") + Swift.print("post id: \(page.postID!), parent id: \(page.parentID ?? 0)") + } + } +} From 3eb5573f278367fae1e63c1b58c6b70e40e36017 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Mon, 8 Jan 2024 17:23:10 +1300 Subject: [PATCH 2/9] Fix #22283: child pages are not moved under parent pages --- WordPress/Classes/Utility/PageTree.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/WordPress/Classes/Utility/PageTree.swift b/WordPress/Classes/Utility/PageTree.swift index ea0a58fa91b4..58239335b200 100644 --- a/WordPress/Classes/Utility/PageTree.swift +++ b/WordPress/Classes/Utility/PageTree.swift @@ -110,7 +110,6 @@ final class PageTree { /// This function assumes none of array elements already exists in the current page tree. func add(_ newPages: [Page]) { let newNodes = newPages.map { TreeNode(page: $0) } - relocateOrphans(to: newNodes) // First try to constrcuture a smaller subtree from the given pages, then move the new subtree to the existing // page tree (`self`). @@ -151,6 +150,8 @@ final class PageTree { } private func add(_ newNodes: [TreeNode]) { + relocateOrphans(to: newNodes) + newNodes.forEach { newNode in let parentID = newNode.pageData.parentID ?? 0 @@ -177,13 +178,17 @@ final class PageTree { /// Move all the nodes in the given argument to the current page tree. private func merge(subtree: PageTree) { - var parentIDs = subtree.nodes.reduce(into: Set()) { $0.insert($1.pageData.parentID ?? 0) } + let subtreeNodes = subtree.nodes + + relocateOrphans(to: subtreeNodes) + + var parentIDs = subtreeNodes.reduce(into: Set()) { $0.insert($1.pageData.parentID ?? 0) } // No need to look for root level parentIDs.remove(0) // Look up parent nodes upfront, to avoid repeated iteration for each node in `subtree`. let parentNodes = findNodes(postIDs: parentIDs) - subtree.nodes.forEach { newNode in + subtreeNodes.forEach { newNode in let parentID = newNode.pageData.parentID ?? 0 // If the new node is at the root level, then simply add it as a child From c7054965959a23ed2f099f03d834634c107f905f Mon Sep 17 00:00:00 2001 From: Tony Li Date: Mon, 8 Jan 2024 17:41:11 +1300 Subject: [PATCH 3/9] Add a release note --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 18667bca6217..a7f725a3b929 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -17,6 +17,7 @@ * [*] Fix an issue with BlogDashboardPersonalizationService being used on the background thread [#22335] * [***] Block Editor: Avoid keyboard dismiss when interacting with text blocks [https://github.com/WordPress/gutenberg/pull/57070] * [**] Block Editor: Auto-scroll upon block insertion [https://github.com/WordPress/gutenberg/pull/57273] +* [**] Fix an issue in Pages List where the pages are not displayed in a hierarchical order [#22338] 23.9 ----- From 3839c2f583071933181e302d77e5c52cae1b28ff Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Mon, 8 Jan 2024 18:07:22 +1100 Subject: [PATCH 4/9] Fix typo in `HierarchyList` name --- WordPress/WordPressTest/PagesListTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/WordPressTest/PagesListTests.swift b/WordPress/WordPressTest/PagesListTests.swift index fa29567fce17..2ded32394e72 100644 --- a/WordPress/WordPressTest/PagesListTests.swift +++ b/WordPress/WordPressTest/PagesListTests.swift @@ -139,7 +139,7 @@ class PagesListTests: CoreDataTestCase { try XCTAssertEqual(XCTUnwrap(sorted.firstIndex(of: parent)) + 1, XCTUnwrap(sorted.firstIndex(of: child))) } - func testHierachyListRepresentationRoundtrip() throws { + func testHierarchyListRepresentationRoundtrip() throws { let roundtrip: (String) throws -> Void = { string in let pages = try Array(hierarchyListRepresentation: string, context: self.mainContext) try XCTAssertEqual(PageTree.hierarchyList(of: pages).hierarchyListRepresentation(), string) @@ -209,8 +209,8 @@ class PagesListTests: CoreDataTestCase { // Compare the two implementions to make sure their results are similar. The pages don'n't need to be in the exact same order, // but each hierachy level should contain the same child pages in it. - let originalList = HierachyList(pages: original) - let newList = HierachyList(pages: new) + let originalList = HierarchyList(pages: original) + let newList = HierarchyList(pages: new) // They have the same hierachy level. XCTAssertEqual(originalList.numberOfLevels, newList.numberOfLevels) @@ -301,7 +301,7 @@ private extension Array where Element == Page { } } -private struct HierachyList { +private struct HierarchyList { let pages: [Page] var numberOfLevels: Int { From 7a834f08ed5fe16db20aa9b32d6fc98a1e6a0624 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Tue, 9 Jan 2024 11:00:09 +1300 Subject: [PATCH 5/9] Simpilify building page tree hierarchy --- WordPress/Classes/Utility/PageTree.swift | 235 ++++------------------- 1 file changed, 33 insertions(+), 202 deletions(-) diff --git a/WordPress/Classes/Utility/PageTree.swift b/WordPress/Classes/Utility/PageTree.swift index 58239335b200..1c92e1613480 100644 --- a/WordPress/Classes/Utility/PageTree.swift +++ b/WordPress/Classes/Utility/PageTree.swift @@ -6,28 +6,18 @@ final class PageTree { var postID: NSNumber? var parentID: NSNumber? } - let pageID: TaggedManagedObjectID - let pageData: PageData + let page: Page var children = [TreeNode]() var parentNode: TreeNode? - init(page: Page, children: [TreeNode] = [], parentNode: TreeNode? = nil) { - self.pageID = TaggedManagedObjectID(page) - self.pageData = PageData(postID: page.postID, parentID: page.parentID) - self.children = children - self.parentNode = parentNode + init(page: Page) { + self.page = page } - // The `PageTree` type is used to loaded - // Some page There are pages They are pages that doesn't belong to the root level, but their parent pages haven't been loaded yet. - var isOrphan: Bool { - (pageData.parentID?.int64Value ?? 0) > 0 && parentNode == nil - } - - func dfsList(in context: NSManagedObjectContext) throws -> [Page] { + func dfsList() -> [Page] { var pages = [Page]() - _ = try depthFirstSearch { level, node in - let page = try context.existingObject(with: node.pageID) + _ = depthFirstSearch { level, node in + let page = node.page page.hierarchyIndex = level page.hasVisibleParent = node.parentNode != nil pages.append(page) @@ -42,18 +32,18 @@ final class PageTree { /// a boolean value indicate whether the search should be stopped. /// - Returns: `true` if search has been stopped by the closure. @discardableResult - func depthFirstSearch(using closure: (Int, TreeNode) throws -> Bool) rethrows -> Bool { - try depthFirstSearch(level: 0, using: closure) + func depthFirstSearch(using closure: (Int, TreeNode) -> Bool) -> Bool { + depthFirstSearch(level: 0, using: closure) } - private func depthFirstSearch(level: Int, using closure: (Int, TreeNode) throws -> Bool) rethrows -> Bool { - let shouldStop = try closure(level, self) + private func depthFirstSearch(level: Int, using closure: (Int, TreeNode) -> Bool) -> Bool { + let shouldStop = closure(level, self) if shouldStop { return true } for child in children { - let shouldStop = try child.depthFirstSearch(level: level + 1, using: closure) + let shouldStop = child.depthFirstSearch(level: level + 1, using: closure) if shouldStop { return true } @@ -61,197 +51,38 @@ final class PageTree { return false } - - /// Perform breadth-first search starting with the current (`self`) node. - /// - /// - Parameter closure: A closure that takes a node as argument and returns a boolean value indicate whether - /// the search should be stopped. - /// - Returns: `true` if search has been stopped by the closure. - func breadthFirstSearch(using closure: (TreeNode) -> Bool) { - var queue = [TreeNode]() - queue.append(self) - while let current = queue.popLast() { - let shouldStop = closure(current) - if shouldStop { - break - } - - queue.append(contentsOf: current.children) - } - } - - func add(_ newNodes: [TreeNode], parentID: NSNumber) -> Bool { - assert(parentID != 0) - - return depthFirstSearch { _, node in - if node.pageData.postID == parentID { - node.children.append(contentsOf: newNodes) - newNodes.forEach { $0.parentNode = node } - return true - } - return false - } - } } - // The top level (or root level) pages, or nodes. - // They can be two types node: - // - child nodes. They are top level pages. - // - orphan nodes. They are pages that doesn't belong to the root level, but their parent pages haven't been loaded yet. - private var nodes = [TreeNode]() - - // `orphanNodes` contains indexes of orphan nodes in the `nodes` array (the value part in the dictionary), which are - // grouped using their parent id (the key part in the dictionary). - // IMPORTANT: Make sure `orphanNodes` is up-to-date after the `nodes` array is modified. - private var orphanNodes = [NSNumber: [Int]]() + static func hierarchyList(of pages: [Page]) -> [Page] { + // An array of `TreeNode` instances that are one-to-one map of the `pages` list. + var nodes: [TreeNode] = [] + // A map of parent page (the dictionary key) to its children (the dictionary value). + var children: [NSNumber: [TreeNode]] = [:] + var allPostIDs: Set = [] - /// Add *new pages* to the page tree. - /// - /// This function assumes none of array elements already exists in the current page tree. - func add(_ newPages: [Page]) { - let newNodes = newPages.map { TreeNode(page: $0) } - - // First try to constrcuture a smaller subtree from the given pages, then move the new subtree to the existing - // page tree (`self`). - // The number of pages in a subtree can be changed if we want to futher tweak the performance. - let batch = 100 - for index in stride(from: 0, to: newNodes.count, by: batch) { - let tree = PageTree() - tree.add(Array(newNodes[index..) -> [NSNumber: TreeNode] { - guard !originalIDs.isEmpty else { - return [:] + return topLevelNodes.reduce(into: []) { + $0.append(contentsOf: $1.dfsList()) } - - var ids = originalIDs - var result = [NSNumber: TreeNode]() - - // The new node is not at the root level, find its parent in the root level nodes. - for child in nodes { - if ids.isEmpty { - break - } - - // Using BFS under the assumption that page tree in most sites is a shallow tree, where most pages are in top layers. - child.breadthFirstSearch { node in - let postID = node.pageData.postID ?? 0 - let foundIndex = ids.firstIndex(of: postID) - if let foundIndex { - ids.remove(at: foundIndex) - result[postID] = node - } - return ids.isEmpty - } - } - - return result - } - - func hierarchyList(in context: NSManagedObjectContext) throws -> [Page] { - try nodes.reduce(into: []) { - try $0.append(contentsOf: $1.dfsList(in: context)) - } - } - - static func hierarchyList(of pages: [Page]) throws -> [Page] { - guard let context = pages.first?.managedObjectContext else { - return [] - } - - let tree = PageTree() - tree.add(pages) - return try tree.hierarchyList(in: context) } } From d087d78a3b5c492ac5df9ab4ed4f3debdc10a498 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Tue, 9 Jan 2024 11:00:45 +1300 Subject: [PATCH 6/9] Revert to more strict comparision --- WordPress/WordPressTest/PagesListTests.swift | 30 ++++++-------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/WordPress/WordPressTest/PagesListTests.swift b/WordPress/WordPressTest/PagesListTests.swift index 2ded32394e72..93e93fa534b9 100644 --- a/WordPress/WordPressTest/PagesListTests.swift +++ b/WordPress/WordPressTest/PagesListTests.swift @@ -196,37 +196,25 @@ class PagesListTests: CoreDataTestCase { start = CFAbsoluteTimeGetCurrent() let original = pages.hierarchySort() + let originalIDs = original.map { $0.postID! } + let originalLevels = original.map { $0.hierarchyIndex } NSLog("hierarchySort took \(String(format: "%.3f", (CFAbsoluteTimeGetCurrent() - start) * 1000)) millisecond to process \(pages.count) pages") start = CFAbsoluteTimeGetCurrent() - let new = try PageTree.hierarchyList(of: pages) + let new = PageTree.hierarchyList(of: pages) + let newIDs = new.map { $0.postID! } + let newLevels = new.map { $0.hierarchyIndex } NSLog("PageTree took \(String(format: "%.3f", (CFAbsoluteTimeGetCurrent() - start) * 1000)) millisecond to process \(pages.count) pages") start = CFAbsoluteTimeGetCurrent() _ = pages.sorted { ($0.postID?.int64Value ?? 0) < ($1.postID?.int64Value ?? 0) } NSLog("Array.sort took \(String(format: "%.3f", (CFAbsoluteTimeGetCurrent() - start) * 1000)) millisecond to process \(pages.count) pages") - // Compare the two implementions to make sure their results are similar. The pages don'n't need to be in the exact same order, - // but each hierachy level should contain the same child pages in it. + let orderDiff = originalIDs.difference(from: newIDs).inferringMoves() + XCTAssertTrue(orderDiff.count == 0, "Unexpected order difference: \(orderDiff)", file: file, line: line) - let originalList = HierarchyList(pages: original) - let newList = HierarchyList(pages: new) - - // They have the same hierachy level. - XCTAssertEqual(originalList.numberOfLevels, newList.numberOfLevels) - - // For each hierachy level, the same child pages are present in both results, without the need of being in the same order. - for level in 1...(originalList.numberOfLevels) { - let pagesAtLevelOriginal = originalList.pages(atLevel: level) - let pagesAtLevelNew = newList.pages(atLevel: level) - XCTAssertEqual(Set(pagesAtLevelOriginal.keys), Set(pagesAtLevelNew.keys), "The parent page ids in each level should be the same") - - for parentPageID in pagesAtLevelOriginal.keys { - let childrenPageIDsOriginal = try XCTUnwrap(pagesAtLevelOriginal[parentPageID]).map { $0.postID } - let childrenPageIDsNew = try XCTUnwrap(pagesAtLevelNew[parentPageID]).map { $0.postID } - XCTAssertEqual(Set(childrenPageIDsOriginal), Set(childrenPageIDsNew), "The children page ids in each level should be the same") - } - } + let levelDiff = originalLevels.difference(from: newLevels).inferringMoves() + XCTAssertTrue(orderDiff.count == 0, "Unexpected level difference: \(orderDiff)", file: file, line: line) } } From b118514b1c3a4f65379cc499dc9d1c37cf776d5c Mon Sep 17 00:00:00 2001 From: Tony Li Date: Tue, 9 Jan 2024 11:08:18 +1300 Subject: [PATCH 7/9] Update view controller to use the new function --- .../Pages/PageListViewController.swift | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Pages/PageListViewController.swift b/WordPress/Classes/ViewRelated/Pages/PageListViewController.swift index dac04f114c0b..4c909d5f5ee4 100644 --- a/WordPress/Classes/ViewRelated/Pages/PageListViewController.swift +++ b/WordPress/Classes/ViewRelated/Pages/PageListViewController.swift @@ -249,7 +249,11 @@ final class PageListViewController: AbstractPostListViewController, UIViewContro do { self.pages = try await buildPageTree(pageIDs: pageIDs) - .hierarchyList(in: coreDataStack.mainContext) + .map { pageID, hierarchyIndex in + let page = try coreDataStack.mainContext.existingObject(with: pageID) + page.hierarchyIndex = hierarchyIndex + return page + } } catch { DDLogError("Failed to reload published pages: \(error)") } @@ -263,7 +267,7 @@ final class PageListViewController: AbstractPostListViewController, UIViewContro /// Build page hierachy in background, which should not take long (less than 2 seconds for 6000+ pages). @MainActor - func buildPageTree(pageIDs: [TaggedManagedObjectID]? = nil, request: NSFetchRequest? = nil) async throws -> PageTree { + func buildPageTree(pageIDs: [TaggedManagedObjectID]? = nil, request: NSFetchRequest? = nil) async throws -> [(pageID: TaggedManagedObjectID, hierarchyIndex: Int)] { assert(pageIDs != nil || request != nil, "`pageIDs` and `request` can not both be nil") let coreDataStack = ContextManager.shared @@ -278,9 +282,9 @@ final class PageListViewController: AbstractPostListViewController, UIViewContro pages = pages.setHomePageFirst() - let tree = PageTree() - tree.add(pages) - return tree + // The `hierarchyIndex` is not a managed property, so it needs to be returend along with the page object id. + return PageTree.hierarchyList(of: pages) + .map { (TaggedManagedObjectID($0), $0.hierarchyIndex) } } } @@ -462,7 +466,13 @@ final class PageListViewController: AbstractPostListViewController, UIViewContro request.predicate = filter.predicate(for: blog, author: .everyone) request.sortDescriptors = filter.sortDescriptors do { - var pages = try await buildPageTree(request: request).hierarchyList(in: ContextManager.shared.mainContext) + let context = ContextManager.shared.mainContext + var pages = try await buildPageTree(request: request) + .map { pageID, hierarchyIndex in + let page = try context.existingObject(with: pageID) + page.hierarchyIndex = hierarchyIndex + return page + } if let index = pages.firstIndex(of: page) { pages = pages.remove(from: index) } From 49fd7b9394d0907007651391f697dce24d0f961d Mon Sep 17 00:00:00 2001 From: Tony Li Date: Tue, 9 Jan 2024 11:37:27 +1300 Subject: [PATCH 8/9] Remove no longer required try keywords --- WordPress/WordPressTest/PagesListTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WordPress/WordPressTest/PagesListTests.swift b/WordPress/WordPressTest/PagesListTests.swift index 93e93fa534b9..9277019552b4 100644 --- a/WordPress/WordPressTest/PagesListTests.swift +++ b/WordPress/WordPressTest/PagesListTests.swift @@ -77,7 +77,7 @@ class PagesListTests: CoreDataTestCase { NSLog("\(pages.count) pages used in \(#function)") measure { - let list = (try? PageTree.hierarchyList(of: pages)) ?? [] + let list = PageTree.hierarchyList(of: pages) XCTAssertEqual(list.count, pages.count) } } @@ -94,7 +94,7 @@ class PagesListTests: CoreDataTestCase { NSLog("\(pages.count) pages used in \(#function)") measure { - let list = (try? PageTree.hierarchyList(of: pages)) ?? [] + let list = PageTree.hierarchyList(of: pages) XCTAssertEqual(list.count, pages.count) } } @@ -125,14 +125,14 @@ class PagesListTests: CoreDataTestCase { let manyPages = parentPage(childrenCount: 17, additionalLevels: 7) // Test 1: place the child page at the begining and the parent page at the end. - var sorted = try PageTree.hierarchyList(of: [child] + manyPages + [parent]) + var sorted = PageTree.hierarchyList(of: [child] + manyPages + [parent]) XCTAssertEqual(parent.hierarchyIndex, 0) XCTAssertEqual(child.hierarchyIndex, 1) // The child page should follow the parent page in the sorted list try XCTAssertEqual(XCTUnwrap(sorted.firstIndex(of: parent)) + 1, XCTUnwrap(sorted.firstIndex(of: child))) // Test 2: place the child page at the end and the parent page at the begining. - sorted = try PageTree.hierarchyList(of: [parent] + manyPages + [child]) + sorted = PageTree.hierarchyList(of: [parent] + manyPages + [child]) XCTAssertEqual(parent.hierarchyIndex, 0) XCTAssertEqual(child.hierarchyIndex, 1) // The child page should follow the parent page in the sorted list @@ -142,7 +142,7 @@ class PagesListTests: CoreDataTestCase { func testHierarchyListRepresentationRoundtrip() throws { let roundtrip: (String) throws -> Void = { string in let pages = try Array(hierarchyListRepresentation: string, context: self.mainContext) - try XCTAssertEqual(PageTree.hierarchyList(of: pages).hierarchyListRepresentation(), string) + XCTAssertEqual(PageTree.hierarchyList(of: pages).hierarchyListRepresentation(), string) } try roundtrip(""" From 55fbfdb25b7c973af27a9c4f885dacaf750b6389 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Tue, 9 Jan 2024 11:40:57 +1300 Subject: [PATCH 9/9] Update PR number in a release note --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index a7f725a3b929..69a5d40070b0 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -17,7 +17,7 @@ * [*] Fix an issue with BlogDashboardPersonalizationService being used on the background thread [#22335] * [***] Block Editor: Avoid keyboard dismiss when interacting with text blocks [https://github.com/WordPress/gutenberg/pull/57070] * [**] Block Editor: Auto-scroll upon block insertion [https://github.com/WordPress/gutenberg/pull/57273] -* [**] Fix an issue in Pages List where the pages are not displayed in a hierarchical order [#22338] +* [**] Fix an issue in Pages List where the pages are not displayed in a hierarchical order [#22345] 23.9 -----