support multiple teamleads in each team (#2702)

* fix jumping avatars
* fix line-break after color dot for long names
This commit is contained in:
Kevin Papst
2021-08-07 18:05:41 +02:00
committed by GitHub
parent e9986c92d6
commit b2d3272151
101 changed files with 1952 additions and 649 deletions

View File

@@ -235,6 +235,8 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
self::assertArrayHasKey('children', $result['errors']);
$data = $result['errors']['children'];
$foundErrors = [];
foreach ($failedFields as $key => $value) {
$messages = [];
$fieldName = $value;
@@ -245,17 +247,25 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
$messages = [$value];
}
}
while (stripos($fieldName, '.') !== false) {
$parts = explode('.', $fieldName);
$tmp = array_shift($parts);
self::assertArrayHasKey($tmp, $data, sprintf('Could not find field "%s" in result', $tmp));
$data = $data[$tmp];
if (\count($data) === 1 && \array_key_exists('children', $data)) {
$data = $data['children'];
}
$fieldName = implode('.', $parts);
}
self::assertArrayHasKey($fieldName, $data, sprintf('Could not find validation error for field "%s" in list: %s', $fieldName, implode(', ', $failedFields)));
self::assertArrayHasKey('errors', $data[$fieldName], sprintf('Field %s has no validation problem', $fieldName));
foreach ($messages as $i => $message) {
self::assertEquals($message, $data[$fieldName]['errors'][$i]);
}
}
$foundErrors = [];
foreach ($data as $fieldName => $field) {
if (\array_key_exists('errors', $field) && \count($field['errors']) > 0) {
$foundErrors[$fieldName] = \count($field['errors']);
if (\array_key_exists('errors', $data[$fieldName]) && \count($data[$fieldName]['errors']) > 0) {
$foundErrors[$fieldName] = \count($data[$fieldName]['errors']);
}
}
@@ -310,6 +320,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
'language' => 'string',
'timezone' => 'string',
'accountNumber' => '@string',
'memberships' => ['result' => 'array', 'type' => 'TeamMembership'],
];
// if a team is embedded
@@ -328,13 +339,28 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
'id' => 'int',
'name' => 'string',
'color' => '@string',
'teamlead' => ['result' => 'object', 'type' => 'User'],
'teamlead' => ['result' => 'object', 'type' => '@User'],
'members' => ['result' => 'array', 'type' => 'TeamMember'],
'users' => ['result' => 'array', 'type' => 'User'],
'customers' => ['result' => 'array', 'type' => '@Customer'],
'projects' => ['result' => 'array', 'type' => '@Project'],
'activities' => ['result' => 'array', 'type' => '@Activity'],
];
// if the team is used inside the team context
case 'TeamMember':
return [
'user' => ['result' => 'object', 'type' => 'User'],
'teamlead' => 'bool',
];
// if the team is used inside the user context
case 'TeamMembership':
return [
'team' => ['result' => 'object', 'type' => 'Team'],
'teamlead' => 'bool',
];
// if a customer is embedded in other objects
case 'Customer':
return [

View File

@@ -160,7 +160,7 @@ class CustomerControllerTest extends APIControllerBaseTest
// and finally a team
$team = new Team();
$team->setName('Testing customer 1 team');
$team->setTeamLead($this->getUserByRole(User::ROLE_USER));
$team->addTeamlead($this->getUserByRole(User::ROLE_USER));
$team->addCustomer($customer);
$team->addProject($project);
$team->addUser($this->getUserByRole(User::ROLE_TEAMLEAD));

View File

@@ -144,7 +144,7 @@ class ProjectControllerTest extends APIControllerBaseTest
// and a team
$team = new Team();
$team->setName('Testing project team');
$team->setTeamLead($this->getUserByRole(User::ROLE_USER));
$team->addTeamlead($this->getUserByRole(User::ROLE_USER));
$team->addCustomer($customer);
$team->addProject($project);
$team->addUser($this->getUserByRole(User::ROLE_TEAMLEAD));

View File

@@ -92,7 +92,9 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'members' => [
['user' => 1, 'teamlead' => 1]
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -123,39 +125,62 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => '',
'teamlead' => 9999,
'members' => [
['user' => 9999, 'teamlead' => 1]
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$response = $client->getResponse();
$this->assertEquals(400, $response->getStatusCode());
$this->assertApiCallValidationError($response, ['name', 'teamlead']);
$this->assertApiCallValidationError($response, ['name', 'members.0.user']);
}
public function testPatchAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'members' => [
['user' => 1, 'teamlead' => true],
['user' => 5, 'teamlead' => true],
]
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
$result = json_decode($client->getResponse()->getContent(), true);
$updateId = $result['id'];
$data = [
'name' => 'foo',
'teamlead' => 2,
'users' => [1, 5, 4]
'members' => [
['user' => 2, 'teamlead' => true],
['user' => 1, 'teamlead' => false],
['user' => 4, 'teamlead' => true],
]
];
$this->request($client, '/api/teams/' . $result['id'], 'PATCH', [], json_encode($data));
$this->request($client, '/api/teams/' . $updateId, 'PATCH', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
self::assertApiResponseTypeStructure('TeamEntity', $result);
$this->assertNotEmpty($result['id']);
self::assertCount(4, $result['users']);
self::assertCount(3, $result['users']);
self::assertFalse($result['members'][1]['teamlead']);
self::assertEquals(1, $result['members'][1]['user']['id']);
self::assertEquals('clara_customer', $result['members'][1]['user']['username']);
self::assertTrue($result['members'][2]['teamlead']);
self::assertEquals(4, $result['members'][2]['user']['id']);
self::assertEquals('tony_teamlead', $result['members'][2]['user']['username']);
self::assertTrue(true, $result['members'][0]['teamlead']);
self::assertEquals(2, $result['members'][0]['user']['id']);
self::assertEquals('john_user', $result['members'][0]['user']['username']);
}
public function testPatchActionWithValidationErrors()
@@ -163,7 +188,9 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'members' => [
['user' => 1, 'teamlead' => 1],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -171,14 +198,15 @@ class TeamControllerTest extends APIControllerBaseTest
$data = [
'name' => '1',
'teamlead' => 9999,
'users' => [9999]
'members' => [
['user' => 9999, 'teamlead' => 1],
],
];
$this->request($client, '/api/teams/' . $result['id'], 'PATCH', [], json_encode($data));
$response = $client->getResponse();
$this->assertEquals(400, $response->getStatusCode());
$this->assertApiCallValidationError($response, ['name', 'teamlead', 'users']);
$this->assertApiCallValidationError($response, ['name', 'members.0.user']);
}
public function testDeleteAction()
@@ -205,7 +233,9 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'members' => [
['user' => 1, 'teamlead' => 1]
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -226,8 +256,10 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -261,8 +293,12 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2, 4, 5]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
['user' => 4, 'teamlead' => 0],
['user' => 5, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -283,8 +319,12 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2, 4, 5]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
['user' => 4, 'teamlead' => 0],
['user' => 5, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -324,7 +364,9 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'members' => [
['user' => 1, 'teamlead' => 1],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -346,8 +388,10 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -383,8 +427,12 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2, 4, 5]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
['user' => 4, 'teamlead' => 0],
['user' => 5, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -411,8 +459,12 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2, 4, 5]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
['user' => 4, 'teamlead' => 0],
['user' => 5, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -442,7 +494,9 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'members' => [
['user' => 1, 'teamlead' => 1],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -463,8 +517,10 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -500,8 +556,12 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2, 4, 5]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
['user' => 4, 'teamlead' => 0],
['user' => 5, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -528,8 +588,12 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2, 4, 5]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
['user' => 4, 'teamlead' => 0],
['user' => 5, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -559,7 +623,9 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'members' => [
['user' => 1, 'teamlead' => 1],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -580,8 +646,10 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -617,8 +685,12 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2, 4, 5]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
['user' => 4, 'teamlead' => 0],
['user' => 5, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -645,8 +717,12 @@ class TeamControllerTest extends APIControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [
'name' => 'foo',
'teamlead' => 1,
'users' => [2, 4, 5]
'members' => [
['user' => 1, 'teamlead' => 1],
['user' => 2, 'teamlead' => 0],
['user' => 4, 'teamlead' => 0],
['user' => 5, 'teamlead' => 0],
],
];
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
$this->assertTrue($client->getResponse()->isSuccessful());

View File

@@ -90,7 +90,7 @@ class DemoteUserCommandTest extends KernelTestCase
$userRepository = $container->get('doctrine')->getRepository(User::class);
$user = $userRepository->loadUserByUsername('tony_teamlead');
self::assertInstanceOf(User::class, $user);
self::assertFalse($user->isTeamlead());
self::assertFalse($user->hasTeamleadRole());
}
public function testDemoteSuper()

View File

@@ -90,7 +90,7 @@ class PromoteUserCommandTest extends KernelTestCase
$userRepository = $container->get('doctrine')->getRepository(User::class);
$user = $userRepository->loadUserByUsername('john_user');
self::assertInstanceOf(User::class, $user);
self::assertTrue($user->isTeamlead());
self::assertTrue($user->hasTeamleadRole());
}
public function testPromoteSuper()

View File

@@ -364,11 +364,7 @@ abstract class ControllerBaseTest extends WebTestCase
*/
protected function assertHasFlashSuccess(HttpKernelBrowser $client, string $message = null)
{
$content = $client->getResponse()->getContent();
self::assertStringContainsString('ALERT.success(\'', $content, 'Could not find flash success message');
if (null !== $message) {
self::assertStringContainsString($message, $content);
}
$this->assertHasFlashMessage($client, 'success', $message);
}
/**
@@ -376,10 +372,18 @@ abstract class ControllerBaseTest extends WebTestCase
* @param string|null $message
*/
protected function assertHasFlashError(HttpKernelBrowser $client, string $message = null)
{
$this->assertHasFlashMessage($client, 'error', $message);
}
private function assertHasFlashMessage(HttpKernelBrowser $client, string $type, string $message = null)
{
$content = $client->getResponse()->getContent();
self::assertStringContainsString('ALERT.error(\'', $content, 'Could not find flash error message');
self::assertStringContainsString('ALERT.' . $type . '(\'', $content, 'Could not find flash ' . $type . ' message');
if (null !== $message) {
// this is a lazy workaround, the templates use the javascript escape filter: |e('js')
// if you ever want to test more complex strings, this logic has to be enhanced
$message = str_replace([' ', ':'], ['\u0020', '\u003A'], $message);
self::assertStringContainsString($message, $content);
}
}

View File

@@ -50,7 +50,7 @@ class ExportControllerTest extends ControllerBaseTest
/** @var Team $team */
$team = new Team();
$team->setName('fooo');
$team->setTeamLead($teamlead);
$team->addTeamlead($teamlead);
$team->addUser($user);
$em->persist($team);
$em->persist($user);

View File

@@ -355,7 +355,6 @@ class ProfileControllerTest extends ControllerBaseTest
public function testTeamsAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$em = $this->getEntityManager();
/** @var User $user */
$user = $this->getUserByRole(User::ROLE_USER);
@@ -371,7 +370,7 @@ class ProfileControllerTest extends ControllerBaseTest
/** @var User $user */
$user = $this->getUserByRole(User::ROLE_USER);
$this->assertEquals([], $user->getTeams()->toArray());
$this->assertEquals([], $user->getTeams());
$form = $client->getCrawler()->filter('form[name=user_teams]')->form();
/** @var ChoiceFormField $team */
@@ -386,10 +385,9 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertHasFlashSuccess($client);
$em = $this->getEntityManager();
$user = $this->getUserByRole(User::ROLE_USER);
$this->assertEquals(1, $user->getTeams()->count());
$this->assertCount(1, $user->getTeams());
}
public function getPreferencesTestData()

View File

@@ -197,7 +197,7 @@ class ProjectControllerTest extends ControllerBaseTest
$project->setEnd(new \DateTime());
$em->persist($project);
$team = new Team();
$team->setTeamLead($this->getUserByRole(User::ROLE_ADMIN));
$team->addTeamlead($this->getUserByRole(User::ROLE_ADMIN));
$team->addProject($project);
$team->setName('project 1');
$em->persist($team);

View File

@@ -79,13 +79,13 @@ class TeamControllerTest extends ControllerBaseTest
$form = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
$this->assertEquals('', $form->get('team_edit_form[name]')->getValue());
$this->assertEquals('5', $form->get('team_edit_form[teamlead]')->getValue());
$client->submit($form, [
'team_edit_form' => [
'name' => 'Test Team' . uniqid(),
]
]);
$values = $form->getPhpValues();
$values['team_edit_form']['name'] = 'Test Team' . uniqid();
$values['team_edit_form']['members'][0]['user'] = 5;
$values['team_edit_form']['members'][0]['teamlead'] = 1;
$client->request($form->getMethod(), $form->getUri(), $values, $form->getPhpFiles());
$this->assertIsRedirect($client, '/edit');
$client->followRedirect();
$this->assertHasFlashSuccess($client);
@@ -105,14 +105,13 @@ class TeamControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$em = $this->getEntityManager();
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$this->importFixture($fixture);
$this->assertAccessIsGranted($client, '/admin/teams/1/edit');
$form = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
$this->assertNotEmpty($form->get('team_edit_form[name]')->getValue());
$client->submit($form, [
'team_edit_form' => [
'name' => 'Test Team 2'
@@ -135,7 +134,6 @@ class TeamControllerTest extends ControllerBaseTest
$this->assertAccessIsGranted($client, '/admin/teams/1/edit_member');
$form = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
$this->assertNotEmpty($form->get('team_edit_form[name]')->getValue());
$client->submit($form, [
'team_edit_form' => [
'name' => 'Test Team 2'

View File

@@ -11,9 +11,9 @@ namespace App\Tests\DataFixtures;
use App\Entity\Customer;
use App\Entity\Team;
use App\Entity\TeamMember;
use App\Entity\User;
use Doctrine\Persistence\ObjectManager;
use Faker\Factory;
/**
* Defines the sample data to load in during controller tests.
@@ -95,7 +95,6 @@ final class TeamFixtures implements TestFixture
{
$created = [];
$faker = Factory::create();
$user = $this->getAllUsers($manager);
$customer = $this->getAllCustomers($manager);
@@ -108,10 +107,14 @@ final class TeamFixtures implements TestFixture
}
}
$member = new TeamMember();
$member->setUser($lead);
$member->setTeamlead(true);
$team = new Team();
$team
->setName($faker->name)
->setTeamLead($lead)
->setName('Testing: ' . uniqid())
->addMember($member)
;
if ($this->addUser) {

View File

@@ -14,6 +14,7 @@ use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Team;
use App\Entity\TeamMember;
use App\Entity\User;
use Doctrine\Common\Collections\Collection;
use PHPUnit\Framework\TestCase;
@@ -28,9 +29,10 @@ class TeamTest extends TestCase
$sut = new Team();
self::assertNull($sut->getId());
self::assertNull($sut->getName());
self::assertNull($sut->getTeamLead());
self::assertInstanceOf(Collection::class, $sut->getUsers());
self::assertEquals(0, $sut->getUsers()->count());
self::assertFalse($sut->hasUsers());
self::assertFalse($sut->hasTeamleads());
self::assertIsArray($sut->getTeamleads());
self::assertEmpty($sut->getTeamleads());
self::assertInstanceOf(Collection::class, $sut->getCustomers());
self::assertEquals(0, $sut->getCustomers()->count());
self::assertInstanceOf(Collection::class, $sut->getProjects());
@@ -54,6 +56,63 @@ class TeamTest extends TestCase
self::assertTrue($sut->hasColor());
}
public function testTeamMemberships()
{
$user = new User();
$user2 = new User();
$member = new TeamMember();
$member->setUser($user);
$member2 = new TeamMember();
$member2->setUser($user);
$member2->setTeam(new Team());
$sut = new Team();
self::assertFalse($sut->isTeamlead($user));
self::assertCount(0, $sut->getTeamleads());
self::assertCount(0, $sut->getMembers());
self::assertFalse($sut->hasMember($member));
$sut->removeMember($member);
$sut->removeMember($member2);
$sut->addMember($member);
self::assertCount(1, $sut->getMembers());
$sut->removeMember($member2);
self::assertCount(1, $sut->getMembers());
$sut->removeMember($member);
self::assertCount(0, $sut->getMembers());
self::assertFalse($sut->isTeamlead($user));
$sut->addMember($member);
$member->setTeamlead(true);
self::assertTrue($sut->isTeamlead($user));
self::assertCount(1, $sut->getMembers());
$sut->addTeamlead($user2);
self::assertCount(2, $sut->getMembers());
self::assertTrue($sut->isTeamlead($user2));
$sut->removeTeamlead($user2);
self::assertCount(2, $sut->getMembers());
self::assertFalse($sut->isTeamlead($user2));
$member21 = new TeamMember();
$member21->setUser($user);
self::assertNull($member21->getTeam());
// this will not actually add it
$sut->addMember($member21);
self::assertSame($sut, $member21->getTeam());
self::assertCount(2, $sut->getMembers());
}
public function testTeamMembershipsException()
{
$this->expectException(\InvalidArgumentException::class);
$sut = new Team();
$member = new TeamMember();
$member->setTeam(new Team());
$sut->addMember($member);
}
public function testSetterAndGetter()
{
$sut = new Team();
@@ -61,12 +120,51 @@ class TeamTest extends TestCase
self::assertEquals('foo-bar', $sut->getName());
self::assertEquals('foo-bar', (string) $sut);
$user = (new User())->setAlias('Foo!');
self::assertInstanceOf(Team::class, $sut->setTeamLead($user));
self::assertSame($user, $sut->getTeamLead());
$user = $this->createMock(User::class);
$user->method('getId')->willReturn(99);
$user->method('getAlias')->willReturn('Foo!');
$sut->addTeamlead($user);
self::assertSame($user, $sut->getTeamleads()[0]);
self::assertTrue($sut->hasTeamleads());
$user1 = $this->createMock(User::class);
$user1->method('getId')->willReturn(1);
$user2 = $this->createMock(User::class);
$user2->method('getId')->willReturn(2);
self::assertFalse($sut->isTeamlead(new User()));
self::assertTrue($sut->isTeamlead($user));
self::assertCount(1, $sut->getTeamleads());
$sut->addTeamlead($user1);
self::assertCount(2, $sut->getTeamleads());
self::assertCount(2, $sut->getMembers());
$sut->addUser($user2);
self::assertCount(3, $sut->getMembers());
self::assertCount(2, $sut->getTeamleads());
$sut->addUser($user2);
self::assertCount(3, $sut->getMembers());
self::assertCount(2, $sut->getTeamleads());
}
/**
* @group legacy
*/
public function testSetterAndGetterDeprecated()
{
$sut = new Team();
self::assertNull($sut->getTeamlead());
self::assertIsArray($sut->getUsers());
self::assertEmpty($sut->getUsers());
$user = (new User())->setAlias('Foo!');
self::assertCount(0, $sut->getUsers());
$sut->setTeamlead($user);
self::assertSame($user, $sut->getTeamlead());
self::assertTrue($sut->hasTeamleads());
self::assertCount(1, $sut->getUsers());
self::assertCount(1, $sut->getTeamleads());
self::assertCount(1, $sut->getMembers());
}
public function testCustomer()
@@ -137,15 +235,69 @@ class TeamTest extends TestCase
$sut = new Team();
$sut->addUser($user);
self::assertEquals(1, $sut->getUsers()->count());
$actual = $sut->getUsers()[0];
self::assertCount(1, $sut->getUsers());
$users = $sut->getUsers();
$actual = $users[0];
self::assertSame($actual, $user);
self::assertSame($sut, $user->getTeams()[0]);
$teams = $user->getTeams();
self::assertSame($sut, $teams[0]);
self::assertFalse($sut->hasUser(new User()));
self::assertTrue($sut->hasUser($user));
$sut->removeUser(new User());
self::assertEquals(1, $sut->getUsers()->count());
self::assertCount(1, $sut->getUsers());
$sut->removeUser($user);
self::assertEquals(0, $sut->getUsers()->count());
self::assertCount(0, $sut->getUsers());
$sut->addTeamlead(new User());
self::assertCount(1, $sut->getUsers());
}
public function testClone()
{
$c = new Customer();
$c->setName('Foo');
$p = new Project();
$p->setName('Bar');
$a = new Activity();
$a->setName('Hello');
$u = new User();
$u->setAlias('World');
$member = new TeamMember();
$member->setUser(new User());
$team = new Team();
$team->addCustomer($c);
$team->addCustomer(new Customer());
$team->addProject($p);
$team->addProject(new Project());
$team->addActivity($a);
$team->addActivity(new Activity());
$team->addTeamlead($u);
$team->addMember($member);
$team->addUser(new User());
$reflection = new \ReflectionClass($team);
$property = $reflection->getProperty('id');
$property->setAccessible(true);
$property->setValue($team, 99);
$property->setAccessible(false);
self::assertEquals(99, $team->getId());
$sut = clone $team;
self::assertNull($sut->getId());
self::assertCount(2, $sut->getCustomers());
self::assertCount(2, $sut->getProjects());
self::assertCount(2, $sut->getActivities());
self::assertCount(1, $sut->getTeamleads());
self::assertCount(3, $sut->getMembers());
self::assertCount(3, $sut->getUsers());
self::assertSame($c, $sut->getCustomers()[0]);
self::assertSame($p, $sut->getProjects()[0]);
self::assertSame($a, $sut->getActivities()[0]);
self::assertSame($u, $sut->getTeamleads()[0]);
self::assertSame($u, $sut->getMembers()[0]->getUser());
self::assertSame($member->getUser(), $sut->getUsers()[1]);
}
}

View File

@@ -11,6 +11,7 @@ namespace App\Tests\Entity;
use App\Constants;
use App\Entity\Team;
use App\Entity\TeamMember;
use App\Entity\User;
use App\Entity\UserPreference;
use App\Export\Spreadsheet\ColumnDefinition;
@@ -198,7 +199,11 @@ class UserTest extends TestCase
self::assertEmpty($sut->getTeams());
self::assertEmpty($team->getUsers());
$sut->addTeam($team);
$member1 = new TeamMember();
$member1->setUser($sut);
$member1->setTeam($team);
$sut->addMembership($member1);
self::assertCount(1, $sut->getTeams());
self::assertSame($team, $sut->getTeams()[0]);
self::assertSame($sut, $team->getUsers()[0]);
@@ -218,12 +223,13 @@ class UserTest extends TestCase
self::assertTrue($sut->isTeamleadOf($team2));
self::assertTrue($sut->isInTeam($team2));
$sut->removeTeam(new Team());
self::assertCount(2, $sut->getTeams());
$sut->removeTeam($team);
$sut->removeMembership(new TeamMember());
self::assertCount(2, $sut->getTeams());
$sut->removeMembership($member1);
self::assertCount(1, $sut->getTeams());
self::assertTrue($sut->hasTeamAssignment());
$sut->removeTeam($team2);
$team2->removeUser($sut);
self::assertCount(0, $sut->getTeams());
self::assertFalse($sut->hasTeamAssignment());
}
@@ -241,7 +247,7 @@ class UserTest extends TestCase
self::assertFalse($sut->isTeamlead());
$sut->addRole(User::ROLE_TEAMLEAD);
self::assertTrue($sut->isTeamlead());
self::assertTrue($sut->hasTeamleadRole());
self::assertFalse($sut->canSeeAllData());
$sut->removeRole(User::ROLE_ADMIN);
@@ -256,7 +262,7 @@ class UserTest extends TestCase
$sut->removeRole(User::ROLE_SUPER_ADMIN);
self::assertFalse($sut->canSeeAllData());
self::assertFalse($sut->isSuperAdmin());
self::assertTrue($sut->isTeamlead());
self::assertTrue($sut->hasTeamleadRole());
$sut->setSuperAdmin(true);
self::assertTrue($sut->isSuperAdmin());
@@ -405,12 +411,74 @@ class UserTest extends TestCase
$unserialized = unserialize($data);
$actual = [
$sut->getUsername(),
$sut->isEnabled(),
$sut->getId(),
$sut->getEmail(),
$unserialized->getUsername(),
$unserialized->isEnabled(),
$unserialized->getId(),
$unserialized->getEmail(),
];
self::assertEquals($expected, $actual);
}
public function testTeamMemberships()
{
$team = new Team();
$team->setName('Foo');
$member = new TeamMember();
$member->setTeam($team);
$member2 = new TeamMember();
$member2->setUser(new User());
$member2->setTeam($team);
$sut = new User();
self::assertFalse($sut->isTeamleadOf($team));
self::assertFalse($sut->isTeamlead());
self::assertCount(0, $sut->getMemberships());
self::assertFalse($sut->hasMembership($member));
$sut->removeMembership($member);
$sut->removeMembership($member2);
$sut->addMembership($member);
self::assertCount(1, $sut->getMemberships());
$sut->removeMembership($member2);
self::assertCount(1, $sut->getMemberships());
$sut->removeMembership($member);
self::assertCount(0, $sut->getMemberships());
self::assertFalse($sut->isTeamleadOf($team));
$sut->addMembership($member);
self::assertCount(1, $sut->getMemberships());
self::assertFalse($sut->isTeamleadOf($team));
$member->setTeamlead(true);
self::assertTrue($sut->isTeamleadOf($team));
$member21 = new TeamMember();
$member21->setTeam($team);
self::assertNull($member21->getUser());
// this will not be added, because $team is already assigned
$sut->addMembership($member21);
self::assertCount(1, $sut->getMemberships());
self::assertSame($sut, $member21->getUser());
$sut->addTeam(new Team());
self::assertCount(2, $sut->getTeams());
self::assertCount(2, $sut->getMemberships());
$sut->removeTeam($team);
self::assertCount(1, $sut->getMemberships());
}
public function testTeamMembershipsException()
{
$this->expectException(\InvalidArgumentException::class);
$sut = new User();
$member = new TeamMember();
$member->setUser(new User());
$sut->addMembership($member);
}
}

View File

@@ -17,13 +17,18 @@ use PHPUnit\Framework\TestCase;
abstract class AbstractLoaderTest extends TestCase
{
protected function getEntityManagerMock(int $createQueryBuilderCount)
protected function getEntityManagerMock(int $createQueryBuilderCount, $resultMock = null)
{
$em = $this->createMock(EntityManager::class);
$qb = $this->createMock(QueryBuilder::class);
$query = $this->createMock(AbstractQuery::class);
$expr = $this->createMock(Expr::class);
if ($resultMock !== null) {
$query->expects($this->any())->method('execute')->willReturn($resultMock);
}
$expr->expects($this->any())->method('isNotNull')->willReturn('');
$expr->expects($this->any())->method('in')->willReturn('');

View File

@@ -9,6 +9,7 @@
namespace App\Tests\Repository\Loader;
use App\Entity\Customer;
use App\Entity\Project;
use App\Repository\Loader\ProjectLoader;
@@ -20,12 +21,18 @@ class ProjectLoaderTest extends AbstractLoaderTest
{
public function testLoadResults()
{
$em = $this->getEntityManagerMock(4);
$sut = new ProjectLoader($em);
$customer = $this->createMock(Customer::class);
$customer->expects($this->once())->method('getId')->willReturn(13);
$entity = $this->createMock(Project::class);
$entity->expects($this->once())->method('getId')->willReturn(1);
$entity->expects($this->once())->method('getCustomer')->willReturn($customer);
$results = [$entity];
$em = $this->getEntityManagerMock(4, $results);
$sut = new ProjectLoader($em);
$sut->loadResults([$entity]);
}

View File

@@ -9,6 +9,7 @@
namespace App\Tests\Repository\Query;
use App\Entity\Team;
use App\Repository\Query\UserQuery;
use App\Repository\Query\VisibilityInterface;
@@ -23,6 +24,7 @@ class UserQueryTest extends BaseQueryTest
$this->assertBaseQuery($sut, 'username');
$this->assertInstanceOf(VisibilityInterface::class, $sut);
$this->assertRole($sut);
$this->assertSearchTeam($sut);
$this->assertResetByFormError(new UserQuery(), 'username');
}
@@ -33,4 +35,14 @@ class UserQueryTest extends BaseQueryTest
$sut->setRole('ROLE_USER');
$this->assertEquals('ROLE_USER', $sut->getRole());
}
protected function assertSearchTeam(UserQuery $sut)
{
$team = new Team();
$this->assertIsArray($sut->getSearchTeams());
$this->assertEmpty($sut->getSearchTeams());
$sut->setSearchTeams([$team, new Team()]);
$this->assertCount(2, $sut->getSearchTeams());
$this->assertSame($team, $sut->getSearchTeams()[0]);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Validator\Constraints;
use App\Entity\Team;
use App\Entity\TeamMember;
use App\Entity\User;
use App\Validator\Constraints\Team as TeamConstraint;
use App\Validator\Constraints\TeamValidator;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
/**
* @covers \App\Validator\Constraints\TeamValidator
*/
class TeamValidatorTest extends ConstraintValidatorTestCase
{
protected function createValidator()
{
return new TeamValidator();
}
public function testConstraintIsInvalid()
{
$this->expectException(UnexpectedTypeException::class);
$this->validator->validate('foo', new NotBlank());
}
public function testMissingTeamlead()
{
$member = new TeamMember();
$member->setTeamlead(false);
$member->setUser(new User());
$team = new Team();
$team->addMember($member);
$this->validator->validate($team, new TeamConstraint());
$this->buildViolation('At least one team leader must be assigned to the team.')
->atPath('property.path.teamleads')
->setCode(TeamConstraint::MISSING_TEAMLEAD)
->assertRaised();
}
}

View File

@@ -75,10 +75,10 @@ abstract class AbstractVoterTest extends TestCase
$rateOther = ['view_rate_other_timesheet', 'edit_rate_other_timesheet'];
$teams = ['view_team', 'create_team', 'edit_team', 'delete_team'];
$roleUser = ['edit_team_activity', 'edit_team_project', 'edit_team_customer'];
$roleTeamlead = ['view_rate_own_timesheet', 'view_rate_other_timesheet', 'hourly-rate_own_profile'];
$roleAdmin = ['hourly-rate_own_profile', 'edit_exported_timesheet'];
$roleSuperAdmin = ['hourly-rate_own_profile', 'hourly-rate_other_profile', 'roles_own_profile', 'system_information', 'system_configuration', 'plugins', 'edit_exported_timesheet'];
$roleUser = ['view_team_member', 'edit_team_activity', 'edit_team_project', 'edit_team_customer'];
$roleTeamlead = ['view_team_member', 'view_rate_own_timesheet', 'view_rate_other_timesheet', 'hourly-rate_own_profile'];
$roleAdmin = ['view_team_member', 'hourly-rate_own_profile', 'edit_exported_timesheet'];
$roleSuperAdmin = ['view_team_member', 'hourly-rate_own_profile', 'hourly-rate_other_profile', 'roles_own_profile', 'system_information', 'system_configuration', 'plugins', 'edit_exported_timesheet'];
$permissions = [
'ROLE_USER' => array_merge($timesheet, $profile, $roleUser),

View File

@@ -90,7 +90,7 @@ class ActivityVoterTest extends AbstractVoterTest
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_TEAMLEAD);
$team->setTeamLead($user);
$team->addTeamlead($user);
$activity = new Activity();
$project = new Project();
@@ -129,7 +129,7 @@ class ActivityVoterTest extends AbstractVoterTest
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->setTeamLead($user);
$team->addTeamlead($user);
$activity = new Activity();
$project = new Project();

View File

@@ -47,7 +47,7 @@ class CustomerVoterTest extends AbstractVoterTest
}
$team = new Team();
$team->setTeamLead($userTeamlead);
$team->addTeamlead($userTeamlead);
foreach ([$userTeamlead] as $user) {
$customer = new Customer();
$team->addCustomer($customer);
@@ -88,7 +88,7 @@ class CustomerVoterTest extends AbstractVoterTest
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_TEAMLEAD);
$team->setTeamLead($user);
$team->addTeamlead($user);
$customer = new Customer();
$customer->addTeam($team);
@@ -101,7 +101,7 @@ class CustomerVoterTest extends AbstractVoterTest
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->setTeamLead($user);
$team->addTeamlead($user);
$customer = new Customer();
$customer->addTeam($team);

View File

@@ -52,7 +52,7 @@ class ProjectVoterTest extends AbstractVoterTest
}
$team = new Team();
$team->setTeamLead($userTeamlead);
$team->addTeamlead($userTeamlead);
foreach ([$userTeamlead] as $user) {
$project = new Project();
$team->addProject($project);
@@ -95,7 +95,7 @@ class ProjectVoterTest extends AbstractVoterTest
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_TEAMLEAD);
$team->setTeamLead($user);
$team->addTeamlead($user);
$project = new Project();
$customer = new Customer();
@@ -117,7 +117,7 @@ class ProjectVoterTest extends AbstractVoterTest
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->setTeamLead($user);
$team->addTeamlead($user);
$project = new Project();
$customer = new Customer();

View File

@@ -111,4 +111,16 @@ class UserVoterTest extends AbstractVoterTest
[User::AUTH_SAML, VoterInterface::ACCESS_DENIED],
];
}
public function testViewTeamMember()
{
$userMock = $this->createMock(User::class);
$userMock->method('getId')->willReturn(1);
$user = new User();
$token = new UsernamePasswordToken($user, 'foo', 'bar', $user->getRoles());
$sut = $this->getVoter(UserVoter::class);
$this->assertEquals(VoterInterface::ACCESS_GRANTED, $sut->vote($token, $user, ['view_team_member']));
$this->assertEquals(VoterInterface::ACCESS_DENIED, $sut->vote($token, $userMock, ['view_team_member']));
}
}