feat: product team members
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
768c814762
commit
fbb4862454
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Product;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
function teamPayload(array $overrides = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'title' => 'Team Product',
|
||||
'description' => 'A product description',
|
||||
'website_url' => 'https://example.com',
|
||||
'category_id' => makeCategory()->id,
|
||||
], $overrides);
|
||||
}
|
||||
|
||||
test('user search returns matching users, requires auth', function () {
|
||||
$this->getJson(route('users.search', ['q' => 'ali']))->assertUnauthorized();
|
||||
|
||||
$viewer = userWithRole('user');
|
||||
$alice = User::factory()->create(['name' => 'Alice Wonder']);
|
||||
User::factory()->create(['name' => 'Bob Builder']);
|
||||
|
||||
$response = $this->actingAs($viewer)->getJson(route('users.search', ['q' => 'lic']))->assertOk();
|
||||
|
||||
expect($response->json())->toHaveCount(1);
|
||||
expect($response->json(0))->toBe(['id' => $alice->id, 'name' => 'Alice Wonder', 'slug' => $alice->slug]);
|
||||
});
|
||||
|
||||
test('user search returns empty for one character queries', function () {
|
||||
$viewer = userWithRole('user');
|
||||
User::factory()->create(['name' => 'Alice Wonder']);
|
||||
|
||||
$this->actingAs($viewer)->getJson(route('users.search', ['q' => 'a']))->assertOk()->assertExactJson([]);
|
||||
});
|
||||
|
||||
test('submitter adds members with roles', function () {
|
||||
$user = userWithRole('user');
|
||||
$a = User::factory()->create();
|
||||
$b = User::factory()->create();
|
||||
|
||||
$this->actingAs($user)->post(route('products.store'), teamPayload([
|
||||
'members' => [
|
||||
['user_id' => $a->id, 'role' => 'Designer'],
|
||||
['user_id' => $b->id, 'role' => 'Engineer'],
|
||||
],
|
||||
]))->assertRedirect()->assertSessionHasNoErrors();
|
||||
|
||||
$members = Product::firstOrFail()->members;
|
||||
expect($members->pluck('id')->all())->toEqualCanonicalizing([$a->id, $b->id]);
|
||||
expect($members->firstWhere('id', $a->id)->pivot->role)->toBe('Designer');
|
||||
});
|
||||
|
||||
test('update replaces members and uses the product owner for the owner rule', function () {
|
||||
$owner = userWithRole('user');
|
||||
$admin = userWithRole('admin');
|
||||
$a = User::factory()->create();
|
||||
$b = User::factory()->create();
|
||||
$product = Product::factory()->pending()->create(['user_id' => $owner->id]);
|
||||
$product->members()->attach($a->id, ['role' => 'Old']);
|
||||
|
||||
$this->actingAs($admin)->put(route('products.update', $product), teamPayload([
|
||||
'category_id' => $product->category_id,
|
||||
'members' => [['user_id' => $owner->id, 'role' => 'Founder']],
|
||||
]))->assertSessionHasErrors('members.0.user_id');
|
||||
expect($product->members()->pluck('users.id')->all())->toBe([$a->id]);
|
||||
|
||||
$this->actingAs($admin)->put(route('products.update', $product), teamPayload([
|
||||
'category_id' => $product->category_id,
|
||||
'members' => [['user_id' => $admin->id, 'role' => 'Helper'], ['user_id' => $b->id, 'role' => 'New']],
|
||||
]))->assertRedirect()->assertSessionHasNoErrors();
|
||||
expect($product->members()->pluck('users.id')->all())->toEqualCanonicalizing([$admin->id, $b->id]);
|
||||
});
|
||||
|
||||
test('submitter cannot be added as a member', function () {
|
||||
$user = userWithRole('user');
|
||||
|
||||
$this->actingAs($user)->post(route('products.store'), teamPayload([
|
||||
'members' => [['user_id' => $user->id, 'role' => 'Founder']],
|
||||
]))->assertSessionHasErrors('members.0.user_id');
|
||||
|
||||
expect(Product::count())->toBe(0);
|
||||
});
|
||||
|
||||
test('same user cannot be added twice', function () {
|
||||
$user = userWithRole('user');
|
||||
$a = User::factory()->create();
|
||||
|
||||
$this->actingAs($user)->post(route('products.store'), teamPayload([
|
||||
'members' => [
|
||||
['user_id' => $a->id, 'role' => 'One'],
|
||||
['user_id' => $a->id, 'role' => 'Two'],
|
||||
],
|
||||
]))->assertSessionHasErrors();
|
||||
|
||||
expect(Product::count())->toBe(0);
|
||||
expect(\DB::table('product_members')->count())->toBe(0);
|
||||
});
|
||||
|
||||
test('unknown user id fails validation', function () {
|
||||
$user = userWithRole('user');
|
||||
|
||||
$this->actingAs($user)->post(route('products.store'), teamPayload([
|
||||
'members' => [['user_id' => 999999, 'role' => 'Ghost']],
|
||||
]))->assertSessionHasErrors('members.0.user_id');
|
||||
|
||||
expect(Product::count())->toBe(0);
|
||||
});
|
||||
|
||||
test('missing role fails validation', function () {
|
||||
$user = userWithRole('user');
|
||||
$a = User::factory()->create();
|
||||
|
||||
$this->actingAs($user)->post(route('products.store'), teamPayload([
|
||||
'members' => [['user_id' => $a->id, 'role' => '']],
|
||||
]))->assertSessionHasErrors('members.0.role');
|
||||
|
||||
expect(Product::count())->toBe(0);
|
||||
});
|
||||
|
||||
test('deleting a user removes their membership', function () {
|
||||
$product = Product::factory()->approved()->create();
|
||||
$member = User::factory()->create();
|
||||
$product->members()->attach($member->id, ['role' => 'Dev']);
|
||||
|
||||
$member->delete();
|
||||
|
||||
expect(\DB::table('product_members')->count())->toBe(0);
|
||||
expect(Product::find($product->id))->not->toBeNull();
|
||||
});
|
||||
|
||||
test('show page lists submitter first as Author then members linking to profiles', function () {
|
||||
$product = Product::factory()->approved()->create();
|
||||
$member = User::factory()->create(['name' => 'Zed Member']);
|
||||
$product->members()->attach($member->id, ['role' => 'Designer']);
|
||||
|
||||
$this->get(route('products.show', $product))->assertOk()
|
||||
->assertSeeInOrder([$product->user->name, 'Author', 'Zed Member', 'Designer'])
|
||||
->assertSee(route('profile.show', $member), false);
|
||||
});
|
||||
|
||||
test('no members shows only the submitter as author', function () {
|
||||
$product = Product::factory()->approved()->create();
|
||||
|
||||
$this->get(route('products.show', $product))->assertOk()
|
||||
->assertSee('Author')
|
||||
->assertDontSee('data-team-members', false);
|
||||
});
|
||||
Reference in New Issue
Block a user