11<?php
22
33use Behat \Behat \Context \Context ;
4+ use Behat \Behat \Hook \Scope \AfterScenarioScope ;
45use Behat \Behat \Hook \Scope \BeforeScenarioScope ;
56use Behat \Gherkin \Node \TableNode ;
67use PHPUnit \Framework \Assert ;
@@ -20,6 +21,9 @@ class BoardContext implements Context {
2021 private array $ storedStacks = [];
2122 private ?array $ activities = null ;
2223
24+ /** @var int[] Board IDs created during the current scenario */
25+ private array $ createdBoardIds = [];
26+
2327 private ServerContext $ serverContext ;
2428
2529 /** @BeforeScenario */
@@ -29,6 +33,24 @@ public function gatherContexts(BeforeScenarioScope $scope) {
2933 $ this ->serverContext = $ environment ->getContext ('ServerContext ' );
3034 }
3135
36+ /** @AfterScenario */
37+ public function cleanupBoards (AfterScenarioScope $ scope ): void {
38+ foreach ($ this ->createdBoardIds as $ boardId ) {
39+ try {
40+ $ this ->requestContext ->sendJSONrequest ('DELETE ' , '/index.php/apps/deck/boards/ ' . $ boardId );
41+ } catch (\Exception $ e ) {
42+ // Ignore cleanup errors
43+ }
44+ }
45+ $ this ->createdBoardIds = [];
46+ }
47+
48+ private function trackBoard (?array $ board ): void {
49+ if (is_array ($ board ) && isset ($ board ['id ' ])) {
50+ $ this ->createdBoardIds [] = $ board ['id ' ];
51+ }
52+ }
53+
3254 public function getLastUsedCard () {
3355 return $ this ->card ;
3456 }
@@ -56,6 +78,7 @@ public function createsABoardNamedWithColor($title, $color) {
5678 ]);
5779 $ this ->getResponse ()->getBody ()->seek (0 );
5880 $ this ->board = json_decode ((string )$ this ->getResponse ()->getBody (), true );
81+ $ this ->trackBoard ($ this ->board );
5982 }
6083
6184 /**
@@ -470,4 +493,167 @@ private function getStack(): mixed {
470493 return $ found ;
471494 }
472495
496+ /**
497+ * @When /^importing a board from CSV file with content$/
498+ */
499+ public function importingABoardFromCsvFileWithContent (\Behat \Gherkin \Node \PyStringNode $ content ) {
500+ $ csvContent = $ content ->getRaw ();
501+ $ this ->requestContext ->sendPlainRequest ('POST ' , '/index.php/apps/deck/boards/import ' , [
502+ 'multipart ' => [
503+ [
504+ 'name ' => 'file ' ,
505+ 'contents ' => $ csvContent ,
506+ 'filename ' => 'import.csv ' ,
507+ 'headers ' => ['Content-Type ' => 'text/csv ' ],
508+ ],
509+ ],
510+ ]);
511+ $ this ->getResponse ()->getBody ()->seek (0 );
512+ $ body = json_decode ((string )$ this ->getResponse ()->getBody (), true );
513+ if ($ this ->getResponse ()->getStatusCode () === 200 && is_array ($ body ) && isset ($ body ['board ' ])) {
514+ $ this ->board = $ body ['board ' ];
515+ $ this ->trackBoard ($ this ->board );
516+ }
517+ }
518+
519+ /**
520+ * @When /^importing CSV cards into the current board with content$/
521+ */
522+ public function importingCsvCardsIntoTheCurrentBoardWithContent (\Behat \Gherkin \Node \PyStringNode $ content ) {
523+ Assert::assertNotNull ($ this ->board , 'Board must be created first ' );
524+ $ csvContent = $ content ->getRaw ();
525+ // Allow tests to reference the last created card's ID via {lastCardId}
526+ if ($ this ->card !== null && isset ($ this ->card ['id ' ])) {
527+ $ csvContent = str_replace ('{lastCardId} ' , (string )$ this ->card ['id ' ], $ csvContent );
528+ }
529+ $ this ->requestContext ->sendPlainRequest ('POST ' , '/index.php/apps/deck/boards/ ' . $ this ->board ['id ' ] . '/importCsv ' , [
530+ 'multipart ' => [
531+ [
532+ 'name ' => 'file ' ,
533+ 'contents ' => $ csvContent ,
534+ 'filename ' => 'cards.csv ' ,
535+ 'headers ' => ['Content-Type ' => 'text/csv ' ],
536+ ],
537+ ],
538+ ]);
539+ $ this ->getResponse ()->getBody ()->seek (0 );
540+ $ body = json_decode ((string )$ this ->getResponse ()->getBody (), true );
541+ if ($ this ->getResponse ()->getStatusCode () === 200 && is_array ($ body ) && isset ($ body ['board ' ])) {
542+ $ this ->board = $ body ['board ' ];
543+ }
544+ }
545+
546+ /**
547+ * @When /^importing a non-CSV file into the current board$/
548+ */
549+ public function importingANonCsvFileIntoTheCurrentBoard () {
550+ Assert::assertNotNull ($ this ->board , 'Board must be created first ' );
551+ $ this ->requestContext ->sendPlainRequest ('POST ' , '/index.php/apps/deck/boards/ ' . $ this ->board ['id ' ] . '/importCsv ' , [
552+ 'multipart ' => [
553+ [
554+ 'name ' => 'file ' ,
555+ 'contents ' => '{"not": "csv"} ' ,
556+ 'filename ' => 'data.json ' ,
557+ 'headers ' => ['Content-Type ' => 'application/json ' ],
558+ ],
559+ ],
560+ ]);
561+ }
562+
563+ /**
564+ * @Then /^the board should have (\d+) stacks$/
565+ */
566+ public function theBoardShouldHaveStacks ($ count ) {
567+ $ this ->requestContext ->sendJSONrequest ('GET ' , '/index.php/apps/deck/stacks/ ' . $ this ->board ['id ' ]);
568+ $ this ->requestContext ->getResponse ()->getBody ()->seek (0 );
569+ $ stacks = json_decode ((string )$ this ->getResponse ()->getBody (), true );
570+ Assert::assertCount ((int )$ count , $ stacks , 'Expected ' . $ count . ' stacks, got ' . count ($ stacks ));
571+ }
572+
573+ /**
574+ * @Then /^the board should have a stack named "([^"]*)"$/
575+ */
576+ public function theBoardShouldHaveAStackNamed ($ name ) {
577+ $ this ->requestContext ->sendJSONrequest ('GET ' , '/index.php/apps/deck/stacks/ ' . $ this ->board ['id ' ]);
578+ $ this ->requestContext ->getResponse ()->getBody ()->seek (0 );
579+ $ stacks = json_decode ((string )$ this ->getResponse ()->getBody (), true );
580+ $ found = array_filter ($ stacks , fn ($ s ) => $ s ['title ' ] === $ name );
581+ Assert::assertNotEmpty ($ found , 'Stack " ' . $ name . '" not found on board ' );
582+ }
583+
584+ /**
585+ * @Then /^the stack "([^"]*)" should have (\d+) cards$/
586+ */
587+ public function theStackShouldHaveCards ($ stackName , $ count ) {
588+ $ this ->requestContext ->sendJSONrequest ('GET ' , '/index.php/apps/deck/stacks/ ' . $ this ->board ['id ' ]);
589+ $ this ->requestContext ->getResponse ()->getBody ()->seek (0 );
590+ $ stacks = json_decode ((string )$ this ->getResponse ()->getBody (), true );
591+ $ found = null ;
592+ foreach ($ stacks as $ stack ) {
593+ if ($ stack ['title ' ] === $ stackName ) {
594+ $ found = $ stack ;
595+ break ;
596+ }
597+ }
598+ Assert::assertNotNull ($ found , 'Stack " ' . $ stackName . '" not found ' );
599+ $ cardCount = count ($ found ['cards ' ] ?? []);
600+ Assert::assertEquals ((int )$ count , $ cardCount , 'Expected ' . $ count . ' cards in stack " ' . $ stackName . '", got ' . $ cardCount );
601+ }
602+
603+ /**
604+ * @Then /^the board should have labels "([^"]*)"$/
605+ */
606+ public function theBoardShouldHaveLabels ($ labelList ) {
607+ $ expectedLabels = array_map ('trim ' , explode (', ' , $ labelList ));
608+ $ this ->requestContext ->sendJSONrequest ('GET ' , '/index.php/apps/deck/boards/ ' . $ this ->board ['id ' ]);
609+ $ this ->requestContext ->getResponse ()->getBody ()->seek (0 );
610+ $ board = json_decode ((string )$ this ->getResponse ()->getBody (), true );
611+ $ actualLabels = array_map (fn ($ l ) => $ l ['title ' ], $ board ['labels ' ] ?? []);
612+ foreach ($ expectedLabels as $ expected ) {
613+ Assert::assertContains ($ expected , $ actualLabels , 'Label " ' . $ expected . '" not found on board. Existing: ' . implode (', ' , $ actualLabels ));
614+ }
615+ }
616+
617+ /**
618+ * @Then /^the card "([^"]*)" should have duedate "([^"]*)"$/
619+ */
620+ public function theCardShouldHaveDuedate ($ cardTitle , $ expectedDuedate ) {
621+ $ card = $ this ->findCardOnBoard ($ cardTitle );
622+ Assert::assertNotNull ($ card , 'Card " ' . $ cardTitle . '" not found on board ' );
623+ Assert::assertNotEmpty ($ card ['duedate ' ], 'Card " ' . $ cardTitle . '" has no duedate set ' );
624+ Assert::assertEquals ($ expectedDuedate , $ card ['duedate ' ], 'Duedate mismatch for card " ' . $ cardTitle . '" ' );
625+ }
626+
627+ /**
628+ * @Then /^the card "([^"]*)" should not have a duedate$/
629+ */
630+ public function theCardShouldNotHaveADuedate ($ cardTitle ) {
631+ $ card = $ this ->findCardOnBoard ($ cardTitle );
632+ Assert::assertNotNull ($ card , 'Card " ' . $ cardTitle . '" not found on board ' );
633+ Assert::assertEmpty ($ card ['duedate ' ], 'Expected no duedate for card " ' . $ cardTitle . '", got " ' . ($ card ['duedate ' ] ?? '' ) . '" ' );
634+ }
635+
636+ /**
637+ * @Then /^the card "([^"]*)" should have description "([^"]*)"$/
638+ */
639+ public function theCardShouldHaveDescription ($ cardTitle , $ expectedDescription ) {
640+ $ card = $ this ->findCardOnBoard ($ cardTitle );
641+ Assert::assertNotNull ($ card , 'Card " ' . $ cardTitle . '" not found on board ' );
642+ Assert::assertEquals ($ expectedDescription , $ card ['description ' ], 'Card description mismatch ' );
643+ }
644+
645+ private function findCardOnBoard (string $ cardTitle ): ?array {
646+ $ this ->requestContext ->sendJSONrequest ('GET ' , '/index.php/apps/deck/stacks/ ' . $ this ->board ['id ' ]);
647+ $ this ->requestContext ->getResponse ()->getBody ()->seek (0 );
648+ $ stacks = json_decode ((string )$ this ->getResponse ()->getBody (), true );
649+ foreach ($ stacks as $ stack ) {
650+ foreach ($ stack ['cards ' ] ?? [] as $ card ) {
651+ if ($ card ['title ' ] === $ cardTitle ) {
652+ return $ card ;
653+ }
654+ }
655+ }
656+ return null ;
657+ }
658+
473659}
0 commit comments