Merge branch 'home-page-create-plan-modal'
This commit is contained in:
commit
ceb956739b
12 changed files with 794 additions and 1 deletions
93
app/Plan/JsonPlanRepository.php
Normal file
93
app/Plan/JsonPlanRepository.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace App\Plan;
|
||||
|
||||
use App\User\User;
|
||||
use App\User\UserRepository;
|
||||
use App\ValueObjects\EmailAddress;
|
||||
|
||||
class JsonPlanRepository implements PlanRepository
|
||||
{
|
||||
private string $filePath;
|
||||
|
||||
public function __construct(
|
||||
private UserRepository $userRepository,
|
||||
) {
|
||||
$this->filePath = __DIR__ . '/../../data/plans.json';
|
||||
}
|
||||
|
||||
public function create(CreatePlanDto $dto): Plan
|
||||
{
|
||||
$plans = $this->readPlans();
|
||||
$id = $this->getNextId($plans);
|
||||
|
||||
$plans[] = [
|
||||
'id' => $id,
|
||||
'name' => $dto->name,
|
||||
'userId' => $dto->user->getId(),
|
||||
];
|
||||
$this->writePlans($plans);
|
||||
|
||||
return new Plan(
|
||||
id: $id,
|
||||
name: $dto->name,
|
||||
user: $dto->user,
|
||||
);
|
||||
}
|
||||
|
||||
public function find(int $id): ?Plan
|
||||
{
|
||||
$plans = $this->readPlans();
|
||||
|
||||
foreach ($plans as $data) {
|
||||
if ($data['id'] === $id) {
|
||||
$user = $this->userRepository->find($data['userId']);
|
||||
if ($user === null) {
|
||||
return null;
|
||||
}
|
||||
return new Plan(
|
||||
id: $data['id'],
|
||||
name: $data['name'],
|
||||
user: $user,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function readPlans(): array
|
||||
{
|
||||
if (!file_exists($this->filePath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$content = file_get_contents($this->filePath);
|
||||
|
||||
return json_decode($content, true) ?? [];
|
||||
}
|
||||
|
||||
private function writePlans(array $plans): void
|
||||
{
|
||||
file_put_contents(
|
||||
$this->filePath,
|
||||
json_encode($plans, JSON_PRETTY_PRINT)
|
||||
);
|
||||
}
|
||||
|
||||
private function getNextId(array $plans): int
|
||||
{
|
||||
if (empty($plans)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$maxId = -1;
|
||||
foreach ($plans as $plan) {
|
||||
if ($plan['id'] > $maxId) {
|
||||
$maxId = $plan['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return $maxId + 1;
|
||||
}
|
||||
}
|
||||
56
app/Plan/PlanController.php
Normal file
56
app/Plan/PlanController.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Plan;
|
||||
|
||||
use App\Exceptions\BadRequestException;
|
||||
use App\Plan\UseCases\CreatePlan;
|
||||
use App\Plan\UseCases\CreatePlanRequest;
|
||||
use DomainException;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class PlanController
|
||||
{
|
||||
public function createPlan(
|
||||
Request $request,
|
||||
Response $response,
|
||||
CreatePlan $createPlanUseCase,
|
||||
): Response {
|
||||
$data = json_decode((string) $request->getBody(), true) ?? [];
|
||||
|
||||
$userId = isset($data['userId']) ? (int) $data['userId'] : null;
|
||||
$textId = isset($data['textId']) ? (int) $data['textId'] : null;
|
||||
$name = $data['name'] ?? null;
|
||||
$dateStart = $data['dateStart'] ?? null;
|
||||
$dateEnd = $data['dateEnd'] ?? null;
|
||||
|
||||
try {
|
||||
$plan = $createPlanUseCase->execute(new CreatePlanRequest(
|
||||
userId: $userId,
|
||||
textId: $textId,
|
||||
name: $name,
|
||||
dateStart: $dateStart,
|
||||
dateEnd: $dateEnd,
|
||||
));
|
||||
} catch (BadRequestException $exception) {
|
||||
$response->getBody()->write(
|
||||
json_encode(['error' => $exception->getMessage()])
|
||||
);
|
||||
return $response->withStatus(400)
|
||||
->withHeader('Content-Type', 'application/json');
|
||||
} catch (DomainException $exception) {
|
||||
$response->getBody()->write(
|
||||
json_encode(['error' => $exception->getMessage()])
|
||||
);
|
||||
return $response->withStatus(404)
|
||||
->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'id' => $plan->getId(),
|
||||
'name' => $plan->getName(),
|
||||
]));
|
||||
return $response->withStatus(201)
|
||||
->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
67
app/ScheduledNode/JsonScheduledNodeRepository.php
Normal file
67
app/ScheduledNode/JsonScheduledNodeRepository.php
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace App\ScheduledNode;
|
||||
|
||||
class JsonScheduledNodeRepository implements ScheduledNodeRepository
|
||||
{
|
||||
private string $filePath;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->filePath = __DIR__ . '/../../data/scheduledNodes.json';
|
||||
}
|
||||
|
||||
public function create(CreateScheduledNodeDto $dto): ScheduledNode
|
||||
{
|
||||
$scheduledNodes = $this->readScheduledNodes();
|
||||
$id = $this->getNextId($scheduledNodes);
|
||||
|
||||
$scheduledNodes[] = [
|
||||
'id' => $id,
|
||||
'date' => $dto->date->format('Y-m-d'),
|
||||
'planId' => $dto->plan->getId(),
|
||||
];
|
||||
$this->writeScheduledNodes($scheduledNodes);
|
||||
|
||||
return new ScheduledNode(
|
||||
id: $id,
|
||||
date: $dto->date,
|
||||
plan: $dto->plan,
|
||||
);
|
||||
}
|
||||
|
||||
private function readScheduledNodes(): array
|
||||
{
|
||||
if (!file_exists($this->filePath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$content = file_get_contents($this->filePath);
|
||||
|
||||
return json_decode($content, true) ?? [];
|
||||
}
|
||||
|
||||
private function writeScheduledNodes(array $scheduledNodes): void
|
||||
{
|
||||
file_put_contents(
|
||||
$this->filePath,
|
||||
json_encode($scheduledNodes, JSON_PRETTY_PRINT)
|
||||
);
|
||||
}
|
||||
|
||||
private function getNextId(array $scheduledNodes): int
|
||||
{
|
||||
if (empty($scheduledNodes)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$maxId = -1;
|
||||
foreach ($scheduledNodes as $scheduledNode) {
|
||||
if ($scheduledNode['id'] > $maxId) {
|
||||
$maxId = $scheduledNode['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return $maxId + 1;
|
||||
}
|
||||
}
|
||||
84
app/User/JsonUserRepository.php
Normal file
84
app/User/JsonUserRepository.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace App\User;
|
||||
|
||||
use App\User\UseCases\CreateUserDto;
|
||||
use App\ValueObjects\EmailAddress;
|
||||
|
||||
class JsonUserRepository implements UserRepository
|
||||
{
|
||||
private string $filePath;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->filePath = __DIR__ . '/../../data/users.json';
|
||||
}
|
||||
|
||||
public function create(CreateUserDto $dto): User
|
||||
{
|
||||
$users = $this->readUsers();
|
||||
$id = $this->getNextId($users);
|
||||
|
||||
$users[] = [
|
||||
'id' => $id,
|
||||
'email' => (string) $dto->email,
|
||||
];
|
||||
$this->writeUsers($users);
|
||||
|
||||
return new User(
|
||||
id: $id,
|
||||
email: $dto->email,
|
||||
);
|
||||
}
|
||||
|
||||
public function find(int $id): ?User
|
||||
{
|
||||
$users = $this->readUsers();
|
||||
|
||||
foreach ($users as $data) {
|
||||
if ($data['id'] === $id) {
|
||||
return new User(
|
||||
id: $data['id'],
|
||||
email: new EmailAddress($data['email']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function readUsers(): array
|
||||
{
|
||||
if (!file_exists($this->filePath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$content = file_get_contents($this->filePath);
|
||||
|
||||
return json_decode($content, true) ?? [];
|
||||
}
|
||||
|
||||
private function writeUsers(array $users): void
|
||||
{
|
||||
file_put_contents(
|
||||
$this->filePath,
|
||||
json_encode($users, JSON_PRETTY_PRINT)
|
||||
);
|
||||
}
|
||||
|
||||
private function getNextId(array $users): int
|
||||
{
|
||||
if (empty($users)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$maxId = -1;
|
||||
foreach ($users as $user) {
|
||||
if ($user['id'] > $maxId) {
|
||||
$maxId = $user['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return $maxId + 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ use DI\Bridge\Slim\Bridge;
|
|||
use App\View\ViewController;
|
||||
use App\Text\TextController;
|
||||
use App\Node\NodeController;
|
||||
use App\Plan\PlanController;
|
||||
|
||||
$container = require __DIR__ . '/container.php';
|
||||
$app = Bridge::create($container);
|
||||
|
|
@ -26,4 +27,6 @@ $app->get('/api/nodes/{textId}', [NodeController::class, 'getNodesOfText']);
|
|||
$app->post('/api/nodes/bulk', [NodeController::class, 'bulkCreateNodes']);
|
||||
$app->post('/api/nodes', [NodeController::class, 'createNode']);
|
||||
|
||||
$app->post('/api/plans', [PlanController::class, 'createPlan']);
|
||||
|
||||
return $app;
|
||||
|
|
|
|||
|
|
@ -6,10 +6,20 @@ use App\Text\TextRepository;
|
|||
use App\Text\JsonTextRepository;
|
||||
use App\Node\NodeRepository;
|
||||
use App\Node\JsonNodeRepository;
|
||||
use App\Plan\PlanRepository;
|
||||
use App\Plan\JsonPlanRepository;
|
||||
use App\User\UserRepository;
|
||||
use App\User\JsonUserRepository;
|
||||
use App\ScheduledNode\ScheduledNodeRepository;
|
||||
use App\ScheduledNode\JsonScheduledNodeRepository;
|
||||
|
||||
$container = new Container([
|
||||
TextRepository::class => DI\autowire(JsonTextRepository::class),
|
||||
NodeRepository::class => DI\autowire(JsonNodeRepository::class),
|
||||
PlanRepository::class => DI\autowire(JsonPlanRepository::class),
|
||||
UserRepository::class => DI\autowire(JsonUserRepository::class),
|
||||
ScheduledNodeRepository::class =>
|
||||
DI\autowire(JsonScheduledNodeRepository::class),
|
||||
]);
|
||||
|
||||
return $container;
|
||||
|
|
|
|||
98
cypress/e2e/homeCreatePlan.cy.js
Normal file
98
cypress/e2e/homeCreatePlan.cy.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
describe('Create plan modal on the home page', () => {
|
||||
beforeEach(() => {
|
||||
cy.exec('npm run db:seed')
|
||||
cy.intercept('GET', '/api/texts').as('getTexts')
|
||||
cy.visit('/home')
|
||||
cy.wait('@getTexts')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cy.exec('npm run db:wipe')
|
||||
})
|
||||
|
||||
it('shows a "Create plan" button on each text', () => {
|
||||
cy.get('#texts-list li').each((textItem) => {
|
||||
cy.wrap(textItem).find('button.create-plan').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('hides the create plan modal by default', () => {
|
||||
cy.get('#create-plan-modal').should('not.be.visible')
|
||||
})
|
||||
|
||||
it('shows the modal when clicking "Create plan"', () => {
|
||||
cy.get('#texts-list li').first()
|
||||
.find('button.create-plan').click()
|
||||
cy.get('#create-plan-modal').should('be.visible')
|
||||
})
|
||||
|
||||
it('modal contains name, date start, date end, save, cancel', () => {
|
||||
cy.get('#texts-list li').first()
|
||||
.find('button.create-plan').click()
|
||||
cy.get('#create-plan-modal input.plan-name').should('be.visible')
|
||||
cy.get('#create-plan-modal input.plan-date-start')
|
||||
.should('be.visible')
|
||||
cy.get('#create-plan-modal input.plan-date-end')
|
||||
.should('be.visible')
|
||||
cy.get('#create-plan-modal button.save-plan').should('be.visible')
|
||||
cy.get('#create-plan-modal button.cancel-plan').should('be.visible')
|
||||
})
|
||||
|
||||
it('hides the modal when clicking "Cancel"', () => {
|
||||
cy.get('#texts-list li').first()
|
||||
.find('button.create-plan').click()
|
||||
cy.get('#create-plan-modal').should('be.visible')
|
||||
cy.get('#create-plan-modal button.cancel-plan').click()
|
||||
cy.get('#create-plan-modal').should('not.be.visible')
|
||||
})
|
||||
|
||||
it('submits plan details to /api/plans', () => {
|
||||
cy.intercept('POST', '/api/plans').as('createPlan')
|
||||
cy.get('#texts-list li').first()
|
||||
.find('button.create-plan').click()
|
||||
cy.get('#create-plan-modal input.plan-name')
|
||||
.type('My reading plan')
|
||||
cy.get('#create-plan-modal input.plan-date-start')
|
||||
.type('2025-01-01')
|
||||
cy.get('#create-plan-modal input.plan-date-end')
|
||||
.type('2025-01-31')
|
||||
cy.get('#create-plan-modal button.save-plan').click()
|
||||
cy.wait('@createPlan').then((createPlanRequest) => {
|
||||
expect(createPlanRequest.response.statusCode).to.eq(201)
|
||||
expect(createPlanRequest.request.body).to.deep.equal({
|
||||
userId: 0,
|
||||
textId: 0,
|
||||
name: 'My reading plan',
|
||||
dateStart: '2025-01-01',
|
||||
dateEnd: '2025-01-31',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('closes the modal after successful submit', () => {
|
||||
cy.intercept('POST', '/api/plans').as('createPlan')
|
||||
cy.get('#texts-list li').first()
|
||||
.find('button.create-plan').click()
|
||||
cy.get('#create-plan-modal input.plan-name')
|
||||
.type('Another plan')
|
||||
cy.get('#create-plan-modal input.plan-date-start')
|
||||
.type('2025-02-01')
|
||||
cy.get('#create-plan-modal input.plan-date-end')
|
||||
.type('2025-02-28')
|
||||
cy.get('#create-plan-modal button.save-plan').click()
|
||||
cy.wait('@createPlan')
|
||||
cy.get('#create-plan-modal').should('not.be.visible')
|
||||
})
|
||||
|
||||
it('does not submit if name is empty', () => {
|
||||
cy.intercept('POST', '/api/plans').as('createPlan')
|
||||
cy.get('#texts-list li').first()
|
||||
.find('button.create-plan').click()
|
||||
cy.get('#create-plan-modal input.plan-date-start')
|
||||
.type('2025-01-01')
|
||||
cy.get('#create-plan-modal input.plan-date-end')
|
||||
.type('2025-01-31')
|
||||
cy.get('#create-plan-modal button.save-plan').click()
|
||||
cy.get('@createPlan.all').should('have.length', 0)
|
||||
})
|
||||
})
|
||||
|
|
@ -28,9 +28,22 @@ $nodes = [
|
|||
],
|
||||
];
|
||||
|
||||
$users = [
|
||||
[
|
||||
'id' => 0,
|
||||
'email' => 'user@example.com',
|
||||
],
|
||||
];
|
||||
|
||||
$plans = [];
|
||||
$scheduledNodes = [];
|
||||
|
||||
$fileDataMap = [
|
||||
'texts.json' => $texts,
|
||||
'nodes.json' => $nodes,
|
||||
'users.json' => $users,
|
||||
'plans.json' => $plans,
|
||||
'scheduledNodes.json' => $scheduledNodes,
|
||||
];
|
||||
|
||||
foreach ($fileDataMap as $file => $data) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
$files = [
|
||||
'texts.json',
|
||||
'nodes.json',
|
||||
'users.json',
|
||||
'plans.json',
|
||||
'scheduledNodes.json',
|
||||
];
|
||||
|
||||
foreach ($files as $file) {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,85 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const textsList = document.getElementById('texts-list');
|
||||
const createPlanModal = document.getElementById('create-plan-modal');
|
||||
|
||||
async function loadTexts() {
|
||||
const response = await fetch('/api/texts');
|
||||
const texts = await response.json();
|
||||
textsList.innerHTML = texts
|
||||
.map(text => '<li>' + text.name + '</li>')
|
||||
.map(text =>
|
||||
'<li>' + text.name +
|
||||
' <button class="create-plan" data-text-id="' +
|
||||
text.id + '">Create plan</button></li>'
|
||||
)
|
||||
.join('');
|
||||
}
|
||||
|
||||
const cancelPlanButton = createPlanModal.querySelector(
|
||||
'button.cancel-plan'
|
||||
);
|
||||
const savePlanButton = createPlanModal.querySelector(
|
||||
'button.save-plan'
|
||||
);
|
||||
const planNameInput = createPlanModal.querySelector('input.plan-name');
|
||||
const planDateStartInput = createPlanModal.querySelector(
|
||||
'input.plan-date-start'
|
||||
);
|
||||
const planDateEndInput = createPlanModal.querySelector(
|
||||
'input.plan-date-end'
|
||||
);
|
||||
|
||||
function openCreatePlanModal(textId) {
|
||||
createPlanModal.dataset.textId = textId;
|
||||
createPlanModal.hidden = false;
|
||||
}
|
||||
|
||||
function closeCreatePlanModal() {
|
||||
createPlanModal.hidden = true;
|
||||
planNameInput.value = '';
|
||||
planDateStartInput.value = '';
|
||||
planDateEndInput.value = '';
|
||||
}
|
||||
|
||||
textsList.addEventListener('click', (clickEvent) => {
|
||||
const createPlanButton = clickEvent.target.closest(
|
||||
'button.create-plan'
|
||||
);
|
||||
if (createPlanButton === null) {
|
||||
return;
|
||||
}
|
||||
openCreatePlanModal(createPlanButton.dataset.textId);
|
||||
});
|
||||
|
||||
cancelPlanButton.addEventListener('click', () => {
|
||||
closeCreatePlanModal();
|
||||
});
|
||||
|
||||
savePlanButton.addEventListener('click', async () => {
|
||||
const planName = planNameInput.value;
|
||||
const dateStart = planDateStartInput.value;
|
||||
const dateEnd = planDateEndInput.value;
|
||||
const textId = Number(createPlanModal.dataset.textId);
|
||||
|
||||
if (planName === '' || dateStart === '' || dateEnd === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch('/api/plans', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
userId: 0,
|
||||
textId: textId,
|
||||
name: planName,
|
||||
dateStart: dateStart,
|
||||
dateEnd: dateEnd,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
closeCreatePlanModal();
|
||||
}
|
||||
});
|
||||
|
||||
loadTexts();
|
||||
});
|
||||
|
|
|
|||
280
tests/e2e/Controllers/PlanControllerTest.php
Normal file
280
tests/e2e/Controllers/PlanControllerTest.php
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\e2e\Controllers;
|
||||
|
||||
use App\Node\CreateNodeDto;
|
||||
use App\Plan\PlanController;
|
||||
use App\Plan\UseCases\CreatePlan;
|
||||
use App\ScheduledNode\UseCases\CreateScheduledNode;
|
||||
use App\Text\CreateTextDto;
|
||||
use App\User\UseCases\CreateUserDto;
|
||||
use App\ValueObjects\EmailAddress;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\Psr7\Factory\ServerRequestFactory;
|
||||
use Slim\Psr7\Factory\StreamFactory;
|
||||
use Slim\Psr7\Response;
|
||||
use Tests\Fakes\FakeNodeRepository;
|
||||
use Tests\Fakes\FakePlanRepository;
|
||||
use Tests\Fakes\FakeScheduledNodeRepository;
|
||||
use Tests\Fakes\FakeTextRepository;
|
||||
use Tests\Fakes\FakeUserRepository;
|
||||
|
||||
class PlanControllerTest extends TestCase
|
||||
{
|
||||
private FakePlanRepository $planRepo;
|
||||
private FakeUserRepository $userRepo;
|
||||
private FakeTextRepository $textRepo;
|
||||
private FakeNodeRepository $nodeRepo;
|
||||
private FakeScheduledNodeRepository $scheduledNodeRepo;
|
||||
private CreatePlan $createPlan;
|
||||
private PlanController $controller;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->planRepo = new FakePlanRepository();
|
||||
$this->userRepo = new FakeUserRepository();
|
||||
$this->textRepo = new FakeTextRepository();
|
||||
$this->nodeRepo = new FakeNodeRepository();
|
||||
$this->scheduledNodeRepo = new FakeScheduledNodeRepository();
|
||||
|
||||
$this->userRepo->create(new CreateUserDto(
|
||||
email: new EmailAddress('test@test.com'),
|
||||
));
|
||||
$text = $this->textRepo->create(new CreateTextDto('testname'));
|
||||
$this->nodeRepo->create(new CreateNodeDto(
|
||||
text: $text,
|
||||
title: 'Root Node',
|
||||
parentNode: null,
|
||||
));
|
||||
|
||||
$createScheduledNode = new CreateScheduledNode(
|
||||
scheduledNodeRepo: $this->scheduledNodeRepo,
|
||||
planRepo: $this->planRepo,
|
||||
);
|
||||
$this->createPlan = new CreatePlan(
|
||||
$this->planRepo,
|
||||
$this->userRepo,
|
||||
$this->textRepo,
|
||||
$this->nodeRepo,
|
||||
$createScheduledNode,
|
||||
);
|
||||
$this->controller = new PlanController();
|
||||
}
|
||||
|
||||
private function makeRequest(array $data): ServerRequestInterface
|
||||
{
|
||||
$body = new StreamFactory()->createStream(json_encode($data));
|
||||
return new ServerRequestFactory()
|
||||
->createServerRequest('POST', 'http://localhost/api/plans')
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->withBody($body);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_201_with_id_and_name(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'textId' => 0,
|
||||
'name' => 'My Plan',
|
||||
'dateStart' => '2025-01-01',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('id', $body);
|
||||
$this->assertEquals('My Plan', $body['name']);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_400_when_user_id_missing(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'textId' => 0,
|
||||
'name' => 'My Plan',
|
||||
'dateStart' => '2025-01-01',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('error', $body);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_400_when_text_id_missing(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'name' => 'My Plan',
|
||||
'dateStart' => '2025-01-01',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('error', $body);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_400_when_name_missing(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'textId' => 0,
|
||||
'dateStart' => '2025-01-01',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('error', $body);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_400_when_date_start_missing(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'textId' => 0,
|
||||
'name' => 'My Plan',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('error', $body);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_400_when_date_end_missing(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'textId' => 0,
|
||||
'name' => 'My Plan',
|
||||
'dateStart' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('error', $body);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_400_when_date_end_before_start(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'textId' => 0,
|
||||
'name' => 'My Plan',
|
||||
'dateStart' => '2025-01-02',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('error', $body);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_404_when_user_not_found(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 99,
|
||||
'textId' => 0,
|
||||
'name' => 'My Plan',
|
||||
'dateStart' => '2025-01-01',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('error', $body);
|
||||
}
|
||||
|
||||
public function test_create_plan_returns_404_when_text_not_found(): void
|
||||
{
|
||||
$response = $this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'textId' => 99,
|
||||
'name' => 'My Plan',
|
||||
'dateStart' => '2025-01-01',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$body = json_decode($response->getBody(), true);
|
||||
$this->assertArrayHasKey('error', $body);
|
||||
}
|
||||
|
||||
public function test_create_plan_persists_plan_in_repository(): void
|
||||
{
|
||||
$this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'textId' => 0,
|
||||
'name' => 'Persistent Plan',
|
||||
'dateStart' => '2025-01-01',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$storedPlan = $this->planRepo->find(0);
|
||||
$this->assertNotNull($storedPlan);
|
||||
$this->assertEquals('Persistent Plan', $storedPlan->getName());
|
||||
}
|
||||
|
||||
public function test_create_plan_schedules_nodes(): void
|
||||
{
|
||||
$this->controller->createPlan(
|
||||
$this->makeRequest([
|
||||
'userId' => 0,
|
||||
'textId' => 0,
|
||||
'name' => 'Scheduling Plan',
|
||||
'dateStart' => '2025-01-01',
|
||||
'dateEnd' => '2025-01-01',
|
||||
]),
|
||||
new Response(),
|
||||
$this->createPlan,
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
1,
|
||||
$this->scheduledNodeRepo->getNumberOfTimesCreateCalled()
|
||||
);
|
||||
$this->assertNotNull($this->scheduledNodeRepo->find(0));
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,20 @@
|
|||
<h1>Home</h1>
|
||||
<ul id="texts-list">
|
||||
</ul>
|
||||
<div id="create-plan-modal" hidden>
|
||||
<h2>Create plan</h2>
|
||||
<label>Name
|
||||
<input class="plan-name" type="text" />
|
||||
</label>
|
||||
<label>Start date
|
||||
<input class="plan-date-start" type="date" />
|
||||
</label>
|
||||
<label>End date
|
||||
<input class="plan-date-end" type="date" />
|
||||
</label>
|
||||
<button class="save-plan">Save</button>
|
||||
<button class="cancel-plan">Cancel</button>
|
||||
</div>
|
||||
<script src="/js/home.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue