Add template switch and reset flows for existing projects

This commit is contained in:
2026-09-08 10:25:52 +00:00
committed by Twyco
parent b8a1d65122
commit 49e887753c
10 changed files with 522 additions and 2 deletions
@@ -2,8 +2,8 @@
namespace Tests\Feature\Projects;
use App\Actions\ProjectTicketMetadata\InitializeProjectTicketMetadata;
use App\Actions\Projects\DuplicateProjectConfiguration;
use App\Actions\ProjectTicketMetadata\InitializeProjectTicketMetadata;
use App\Models\BoardColumn;
use App\Models\NotificationAction;
use App\Models\Project;
@@ -590,6 +590,149 @@ class ProjectControllerTest extends TestCase
);
}
public function test_project_admins_can_switch_projects_to_another_template(): void
{
$user = User::factory()->create();
$project = Project::factory()->create([
'key' => 'CORE',
'name' => 'Core Platform',
'show_author' => false,
'show_story_points' => false,
]);
$currentTemplate = ProjectTemplate::factory()->create([
'name' => 'Current Template',
'slug' => 'current-template',
'copy_areas' => [
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS,
DuplicateProjectConfiguration::COPY_TICKET_TYPES,
],
'configuration_snapshot' => [
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS => [
'show_author' => false,
'show_story_points' => false,
],
DuplicateProjectConfiguration::COPY_TICKET_TYPES => [
['name' => 'Bug', 'position' => 1, 'is_parent' => false],
],
],
]);
$newTemplate = ProjectTemplate::factory()->create([
'name' => 'Operations Template',
'slug' => 'operations-template',
'copy_areas' => [
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS,
DuplicateProjectConfiguration::COPY_TICKET_TYPES,
],
'configuration_snapshot' => [
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS => [
'show_author' => true,
'show_story_points' => true,
],
DuplicateProjectConfiguration::COPY_TICKET_TYPES => [
['name' => 'Incident', 'position' => 1, 'is_parent' => false],
['name' => 'Maintenance', 'position' => 2, 'is_parent' => false],
],
],
]);
$project->update(['project_template_id' => $currentTemplate->id]);
$project->ticketTypes()->delete();
TicketType::factory()->create(['project_id' => $project->id, 'name' => 'Bug', 'position' => 1, 'is_parent' => false]);
$this->syncProjectPermissions($project, $user, ['admin']);
$this->actingAs($user)
->from(route('projects.settings.general', $project))
->post(route('projects.settings.general.switch-template', $project), [
'confirmation_key' => 'CORE',
'source_template_id' => $newTemplate->id,
])
->assertRedirect(route('projects.settings.general', $project))
->assertSessionHas('status', 'Core Platform now uses the Operations Template template.');
$project->refresh();
$this->assertSame($newTemplate->id, $project->project_template_id);
$this->assertTrue($project->show_author);
$this->assertTrue($project->show_story_points);
$this->assertSame(['Incident', 'Maintenance'], $project->ticketTypes()->pluck('name')->all());
}
public function test_project_admins_can_reset_projects_to_their_current_template_baseline(): void
{
$user = User::factory()->create();
$project = Project::factory()->create([
'key' => 'CORE',
'name' => 'Core Platform',
'show_author' => false,
'show_story_points' => false,
]);
$template = ProjectTemplate::factory()->create([
'name' => 'Workflow Template',
'slug' => 'workflow-template',
'copy_areas' => [
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS,
DuplicateProjectConfiguration::COPY_TICKET_TYPES,
],
'configuration_snapshot' => [
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS => [
'show_author' => true,
'show_story_points' => true,
],
DuplicateProjectConfiguration::COPY_TICKET_TYPES => [
['name' => 'Bug', 'position' => 1, 'is_parent' => false],
['name' => 'Feature', 'position' => 2, 'is_parent' => false],
],
],
]);
$project->update(['project_template_id' => $template->id]);
$project->ticketTypes()->delete();
TicketType::factory()->create(['project_id' => $project->id, 'name' => 'Custom', 'position' => 1, 'is_parent' => false]);
$this->syncProjectPermissions($project, $user, ['admin']);
$this->actingAs($user)
->from(route('projects.settings.general', $project))
->post(route('projects.settings.general.reset-template', $project), [
'confirmation_key' => 'CORE',
])
->assertRedirect(route('projects.settings.general', $project))
->assertSessionHas('status', 'Core Platform was reset to the Workflow Template template baseline.');
$project->refresh();
$this->assertSame($template->id, $project->project_template_id);
$this->assertTrue($project->show_author);
$this->assertTrue($project->show_story_points);
$this->assertSame(['Bug', 'Feature'], $project->ticketTypes()->pluck('name')->all());
}
public function test_project_template_switch_requires_exact_project_key_confirmation(): void
{
$user = User::factory()->create();
$project = Project::factory()->create([
'key' => 'CORE',
]);
$project->update([
'project_template_id' => ProjectTemplate::factory()->create()->id,
]);
$newTemplate = ProjectTemplate::factory()->create();
$this->syncProjectPermissions($project, $user, ['admin']);
$this->actingAs($user)
->from(route('projects.settings.general', $project))
->post(route('projects.settings.general.switch-template', $project), [
'confirmation_key' => 'core',
'source_template_id' => $newTemplate->id,
])
->assertRedirect(route('projects.settings.general', $project))
->assertSessionHasErrors('confirmation_key');
$this->assertNotSame($newTemplate->id, $project->fresh()->project_template_id);
}
/**
* Ensure admins can delete projects and their dependent project-scoped data.
*/