'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); });