Skip to content

Commit

Permalink
fix(hub): add special handling for devquest sets hub breadcrumbs (#2947)
Browse files Browse the repository at this point in the history
  • Loading branch information
wescopeland authored Dec 19, 2024
1 parent 8f70560 commit 4709431
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
46 changes: 46 additions & 0 deletions app/Platform/Actions/BuildHubBreadcrumbsAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ public function execute(GameSet $gameSet): array
$remainingPath = [];
$currentGameSet = $gameSet;

// Handle "[DevQuest N Sets] Title".
if (preg_match('/^\[DevQuest \d+ Sets\]/', $currentGameSet->title)) {
return $this->handleDevQuestPath($gameSet);
}

// Extract the type from the title if it matches the pattern "[Type - ...]".
if (preg_match('/^\[(.*?) - /', $currentGameSet->title, $matches)) {
$currentType = $matches[1];
Expand Down Expand Up @@ -231,6 +236,47 @@ private function buildDifficultyBreadcrumbs(GameSet $gameSet): array
return $breadcrumbs;
}

/**
* Handles the special case of DevQuest paths.
* These often have titles like "[DevQuest 021 Sets] Homebrew Heaven".
* Notice the square brackets are oddly-placed.
*/
private function handleDevQuestPath(GameSet $gameSet): array
{
// Add items in reverse order. We want them to appear as:
// [Central] -> [Central - Developer Events] -> [Dev Events - DevQuest] -> [DevQuest Sets]

$remainingPath = [$this->toPathArray($gameSet)];

// Find and add the DevQuest hub.
$devQuestHub = GameSet::where('title', '[Dev Events - DevQuest]')
->where('type', GameSetType::Hub)
->whereNull('deleted_at')
->first();

if ($devQuestHub) {
array_unshift($remainingPath, $this->toPathArray($devQuestHub));

// Find and add the Developer Events hub.
$devEventsHub = GameSet::where('title', '[Central - Developer Events]')
->where('type', GameSetType::Hub)
->whereNull('deleted_at')
->first();

if ($devEventsHub) {
array_unshift($remainingPath, $this->toPathArray($devEventsHub));
}
}

// Add Central hub at the start of the path.
$centralHub = GameSet::centralHub()->first();
if ($centralHub) {
array_unshift($remainingPath, $this->toPathArray($centralHub));
}

return $remainingPath;
}

/**
* Process custom hierarchies for types not defined in `HUB_HIERARCHY`.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,38 @@ describe('Component: HubBreadcrumbs', () => {
expect(screen.queryByText(/meta\|qa/i)).not.toBeInTheDocument();
expect(screen.queryByText(/subgenre/i)).not.toBeInTheDocument();
});

it('formats DevQuest Sets titles correctly', () => {
// ARRANGE
const breadcrumbs = [
createGameSet({ title: '[Central]' }),
createGameSet({ title: '[Central - Developer Events]' }),
createGameSet({ title: '[Dev Events - DevQuest]' }),
createGameSet({ title: '[DevQuest 021 Sets] Homebrew Heaven' }),
];

render(<HubBreadcrumbs breadcrumbs={breadcrumbs} />);

// ASSERT
expect(screen.getByText(/all hubs/i)).toBeVisible();
expect(screen.getByText(/developer events/i)).toBeVisible();
expect(screen.getByText(/devquest/i)).toBeVisible();
expect(screen.getByText('21: Homebrew Heaven')).toBeVisible();

expect(screen.queryByText(/devquest sets/i)).not.toBeInTheDocument();
});

it('given a malformed DevQuest title, handles it gracefully', () => {
// ARRANGE
const breadcrumbs = [
createGameSet({ title: '[DevQuest Sets]' }),
createGameSet({ title: '[DevQuest foo Sets] After Bracket' }),
];

render(<HubBreadcrumbs breadcrumbs={breadcrumbs} />);

// ASSERT
expect(screen.getByText('DevQuest Sets')).toBeVisible();
expect(screen.getByText('After Bracket')).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,23 @@ function useCleanBreadcrumbHubTitles() {

const cleaned = cleanHubTitle(title);

/**
* "[DevQuest 021 Sets]: Homebrew Heaven" -> "21: Homebrew Heaven"
* Extract the quest number and content after the bracket.
* Format as "N: Title".
*/
if (cleaned.includes('DevQuest') && cleaned.includes('Sets]')) {
const match = cleaned.match(/DevQuest (\d+)/);
const number = match ? parseInt(match[1], 10) : null;

const parts = cleaned.split(']');
if (parts.length > 1) {
const content = parts[1].trim();

return number ? `${number}: ${content}` : content;
}
}

// Always strip organizational prefixes first.
const alwaysStripPrefixes = [
'ASB -',
Expand Down
35 changes: 35 additions & 0 deletions tests/Feature/Platform/Action/BuildHubBreadcrumbsActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -485,4 +485,39 @@ public function testItPreventsDuplicateCentralEventsHub(): void
));
$this->assertEquals(1, $centralEventsCount);
}

public function testItHandlesDevQuestSetsFormat(): void
{
// Arrange
$centralHub = $this->createHub('[Central]');
$centralHub->id = GameSet::CentralHubId;
$centralHub->save();

$devEventsHub = $this->createHub('[Central - Developer Events]');
$devQuestHub = $this->createHub('[Dev Events - DevQuest]');
$devQuestSetHub = $this->createHub('[DevQuest 018 Sets] Subset Station');

GameSetLink::factory()->create([
'parent_game_set_id' => $centralHub->id,
'child_game_set_id' => $devEventsHub->id,
]);
GameSetLink::factory()->create([
'parent_game_set_id' => $devEventsHub->id,
'child_game_set_id' => $devQuestHub->id,
]);
GameSetLink::factory()->create([
'parent_game_set_id' => $devQuestHub->id,
'child_game_set_id' => $devQuestSetHub->id,
]);

// Act
$breadcrumbs = $this->action->execute($devQuestSetHub);

// Assert
$this->assertCount(4, $breadcrumbs);
$this->assertBreadcrumb($breadcrumbs[0], GameSet::CentralHubId, '[Central]');
$this->assertBreadcrumb($breadcrumbs[1], $devEventsHub->id, '[Central - Developer Events]');
$this->assertBreadcrumb($breadcrumbs[2], $devQuestHub->id, '[Dev Events - DevQuest]');
$this->assertBreadcrumb($breadcrumbs[3], $devQuestSetHub->id, '[DevQuest 018 Sets] Subset Station');
}
}

0 comments on commit 4709431

Please sign in to comment.