1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace EHAERER \PasteReference \Tests \Functional ;
6+
7+ /***************************************************************
8+ * Copyright notice
9+ * (c) 2024 Ephraim Härer <mail@ephra.im>
10+ * All rights reserved
11+ * This script is part of the TYPO3 project. The TYPO3 project is
12+ * free software; you can redistribute it and/or modify
13+ * it under the terms of the GNU General Public License as published by
14+ * the Free Software Foundation; either version 2 of the License, or
15+ * (at your option) any later version.
16+ * The GNU General Public License can be found at
17+ * http://www.gnu.org/copyleft/gpl.html.
18+ * This script is distributed in the hope that it will be useful,
19+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+ * GNU General Public License for more details.
22+ * This copyright notice MUST APPEAR in all copies of the script!
23+ ***************************************************************/
24+
25+ use EHAERER \PasteReference \DataHandler \ProcessCmdmap ;
26+ use EHAERER \PasteReference \EventListener \AfterTcaCompilationEventListener ;
27+ use EHAERER \PasteReference \Helper \Helper ;
28+ use PHPUnit \Framework \Attributes \Test ;
29+ use TYPO3 \CMS \Core \Configuration \Event \AfterTcaCompilationEvent ;
30+ use TYPO3 \CMS \Core \Configuration \ExtensionConfiguration ;
31+ use TYPO3 \CMS \Core \Database \ConnectionPool ;
32+ use TYPO3 \CMS \Core \DataHandling \DataHandler ;
33+ use TYPO3 \CMS \Core \Information \Typo3Version ;
34+ use TYPO3 \CMS \Core \Utility \ExtensionManagementUtility ;
35+ use TYPO3 \CMS \Core \Utility \GeneralUtility ;
36+ use TYPO3 \TestingFramework \Core \Functional \FunctionalTestCase ;
37+
38+ /**
39+ * Test TYPO3 API compatibility across versions v13 and v14
40+ */
41+ final class ApiCompatibilityTest extends FunctionalTestCase
42+ {
43+ protected array $ testExtensionsToLoad = [
44+ 'ehaerer/paste-reference ' ,
45+ ];
46+
47+ private Typo3Version $ typo3Version ;
48+
49+ protected function setUp (): void
50+ {
51+ parent ::setUp ();
52+ $ this ->importCSVDataSet (__DIR__ . '/../Fixtures/tt_content.csv ' );
53+ $ this ->typo3Version = GeneralUtility::makeInstance (Typo3Version::class);
54+ }
55+
56+ #[Test]
57+ public function extensionIsLoadedInCurrentTypo3Version (): void
58+ {
59+ self ::assertTrue (ExtensionManagementUtility::isLoaded ('paste_reference ' ));
60+
61+ $ typo3Version = GeneralUtility::makeInstance (Typo3Version::class);
62+ $ majorVersion = $ typo3Version ->getMajorVersion ();
63+
64+ // Verify extension works with supported TYPO3 versions
65+ self ::assertContains ($ majorVersion , [13 , 14 ], 'Extension should work with TYPO3 v13 and v14 ' );
66+ }
67+
68+ #[Test]
69+ public function connectionPoolApiIsCompatible (): void
70+ {
71+ $ connectionPool = GeneralUtility::makeInstance (ConnectionPool::class);
72+ $ queryBuilder = $ connectionPool ->getQueryBuilderForTable ('tt_content ' );
73+
74+ // Test that ConnectionPool API works consistently across versions
75+ self ::assertInstanceOf (\TYPO3 \CMS \Core \Database \Query \QueryBuilder::class, $ queryBuilder );
76+
77+ // Test query builder methods that are used by the extension
78+ $ connection = $ connectionPool ->getConnectionForTable ('tt_content ' );
79+ self ::assertInstanceOf (\TYPO3 \CMS \Core \Database \Connection::class, $ connection );
80+
81+ // Test restrictions API
82+ $ restrictions = $ queryBuilder ->getRestrictions ();
83+ self ::assertInstanceOf (\TYPO3 \CMS \Core \Database \Query \Restriction \QueryRestrictionContainerInterface::class, $ restrictions );
84+ }
85+
86+ #[Test]
87+ public function dataHandlerApiIsCompatible (): void
88+ {
89+ $ dataHandler = GeneralUtility::makeInstance (DataHandler::class);
90+
91+ // Test DataHandler properties and methods used by extension
92+ self ::assertObjectHasProperty ('isImporting ' , $ dataHandler );
93+
94+ // Test method signatures that are used by ProcessCmdmap
95+ self ::assertTrue (method_exists ($ dataHandler , 'start ' ));
96+ self ::assertTrue (method_exists ($ dataHandler , 'process_datamap ' ));
97+ self ::assertTrue (method_exists ($ dataHandler , 'process_cmdmap ' ));
98+ }
99+
100+ #[Test]
101+ public function helperClassUsesCompatibleApis (): void
102+ {
103+ $ helper = GeneralUtility::makeInstance (Helper::class);
104+
105+ // Test that Helper class can be instantiated and uses compatible APIs
106+ self ::assertInstanceOf (Helper::class, $ helper );
107+
108+ // Test database query methods
109+ $ queryBuilder = $ helper ->getQueryBuilder ('tt_content ' );
110+ self ::assertInstanceOf (\TYPO3 \CMS \Core \Database \Query \QueryBuilder::class, $ queryBuilder );
111+
112+ // Test backend user access
113+ $ backendUser = $ helper ->getBackendUser ();
114+ // Backend user might be null in testing context, but method should exist
115+ self ::assertTrue (method_exists ($ helper , 'getBackendUser ' ));
116+
117+ // Test language service access
118+ self ::assertTrue (method_exists ($ helper , 'getLanguageService ' ));
119+ }
120+
121+ #[Test]
122+ public function processCmdmapUsesCompatibleApis (): void
123+ {
124+ $ processCmdmap = GeneralUtility::makeInstance (ProcessCmdmap::class);
125+ $ dataHandler = GeneralUtility::makeInstance (DataHandler::class);
126+
127+ // Test initialization with compatible parameters
128+ $ processCmdmap ->init ('tt_content ' , 1 , $ dataHandler );
129+
130+ self ::assertEquals ('tt_content ' , $ processCmdmap ->getTable ());
131+ self ::assertEquals (1 , $ processCmdmap ->getPageUid ());
132+ self ::assertSame ($ dataHandler , $ processCmdmap ->getDataHandler ());
133+ }
134+
135+ #[Test]
136+ public function extensionConfigurationApiIsCompatible (): void
137+ {
138+ $ extensionConfiguration = GeneralUtility::makeInstance (ExtensionConfiguration::class);
139+
140+ // Test that extension configuration can be retrieved
141+ try {
142+ $ config = $ extensionConfiguration ->get ('paste_reference ' );
143+ // Configuration might be empty, but method should work
144+ self ::assertIsArray ($ config );
145+ } catch (\TYPO3 \CMS \Core \Configuration \Exception \ExtensionConfigurationExtensionNotConfiguredException $ e ) {
146+ // This is acceptable in test environment
147+ self ::assertStringContainsString ('paste_reference ' , $ e ->getMessage ());
148+ }
149+ }
150+
151+ #[Test]
152+ public function tcaEventListenerIsCompatible (): void
153+ {
154+ $ listener = GeneralUtility::makeInstance (AfterTcaCompilationEventListener::class);
155+
156+ // Create a mock TCA array
157+ $ tca = [
158+ 'tt_content ' => [
159+ 'types ' => [
160+ 'shortcut ' => []
161+ ]
162+ ]
163+ ];
164+
165+ $ event = new AfterTcaCompilationEvent ($ tca );
166+
167+ // Test that event listener can process TCA without errors
168+ try {
169+ $ listener ($ event );
170+ self ::assertTrue (true , 'TCA event listener executed without errors ' );
171+ } catch (\TYPO3 \CMS \Core \Configuration \Exception \ExtensionConfigurationExtensionNotConfiguredException $ e ) {
172+ // This is acceptable when extension configuration is not set
173+ self ::assertStringContainsString ('paste_reference ' , $ e ->getMessage ());
174+ }
175+ }
176+
177+ #[Test]
178+ public function globalVariablesAreAccessibleAcrossVersions (): void
179+ {
180+ // Test TYPO3_REQUEST global (used in ProcessCmdmap)
181+ // In test environment, this might not be set, but we test the access pattern
182+ $ request = $ GLOBALS ['TYPO3_REQUEST ' ] ?? null ;
183+
184+ // The extension should handle null request gracefully
185+ self ::assertTrue (true , 'Global variables access pattern is compatible ' );
186+
187+ // Test BE_USER global access pattern
188+ $ backendUser = $ GLOBALS ['BE_USER ' ] ?? null ;
189+ self ::assertTrue (true , 'Backend user global access pattern is compatible ' );
190+
191+ // Test LANG global access pattern
192+ $ languageService = $ GLOBALS ['LANG ' ] ?? null ;
193+ self ::assertTrue (true , 'Language service global access pattern is compatible ' );
194+ }
195+
196+ #[Test]
197+ public function databaseQueryRestrictionsAreCompatible (): void
198+ {
199+ $ helper = GeneralUtility::makeInstance (Helper::class);
200+ $ queryBuilder = $ helper ->getQueryBuilder ('tt_content ' );
201+
202+ // Test that restriction removal methods exist and work
203+ $ restrictions = $ queryBuilder ->getRestrictions ();
204+
205+ // These restriction classes should exist in both TYPO3 v13 and v14
206+ $ restrictionClasses = [
207+ \TYPO3 \CMS \Core \Database \Query \Restriction \HiddenRestriction::class,
208+ \TYPO3 \CMS \Core \Database \Query \Restriction \StartTimeRestriction::class,
209+ \TYPO3 \CMS \Core \Database \Query \Restriction \EndTimeRestriction::class,
210+ ];
211+
212+ foreach ($ restrictionClasses as $ restrictionClass ) {
213+ self ::assertTrue (class_exists ($ restrictionClass ), "Restriction class {$ restrictionClass } should exist " );
214+ }
215+
216+ // Test that removeByType method exists
217+ self ::assertTrue (method_exists ($ restrictions , 'removeByType ' ));
218+ }
219+
220+ #[Test]
221+ public function backendUtilityMethodsAreCompatible (): void
222+ {
223+ // Test BackendUtility methods used by the extension
224+ self ::assertTrue (class_exists (\TYPO3 \CMS \Backend \Utility \BackendUtility::class));
225+ self ::assertTrue (method_exists (\TYPO3 \CMS \Backend \Utility \BackendUtility::class, 'getRecordTitle ' ));
226+ }
227+
228+ #[Test]
229+ public function generalUtilityMethodsAreCompatible (): void
230+ {
231+ // Test GeneralUtility methods used throughout the extension
232+ self ::assertTrue (method_exists (GeneralUtility::class, 'makeInstance ' ));
233+ self ::assertTrue (method_exists (GeneralUtility::class, 'fixed_lgd_cs ' ));
234+
235+ // Test that makeInstance works with extension classes
236+ $ helper = GeneralUtility::makeInstance (Helper::class);
237+ self ::assertInstanceOf (Helper::class, $ helper );
238+ }
239+
240+ #[Test]
241+ public function shortcutPreviewRendererApiIsCompatible (): void
242+ {
243+ // Test that ShortcutPreviewRenderer can be instantiated
244+ $ renderer = GeneralUtility::makeInstance (\EHAERER \PasteReference \PageLayoutView \ShortcutPreviewRenderer::class);
245+ self ::assertInstanceOf (\EHAERER \PasteReference \PageLayoutView \ShortcutPreviewRenderer::class, $ renderer );
246+
247+ // Test that it implements required interfaces
248+ self ::assertInstanceOf (\TYPO3 \CMS \Backend \Preview \PreviewRendererInterface::class, $ renderer );
249+ self ::assertInstanceOf (\TYPO3 \CMS \Backend \Preview \StandardContentPreviewRenderer::class, $ renderer );
250+
251+ $ majorVersion = $ this ->typo3Version ->getMajorVersion ();
252+
253+ // Test version-specific API availability
254+ if ($ majorVersion >= 14 ) {
255+ // Test TYPO3 v14+ specific APIs
256+ self ::assertTrue (class_exists (\TYPO3 \CMS \Core \Domain \RecordFactory::class), 'RecordFactory should exist in v14+ ' );
257+ self ::assertTrue (interface_exists (\TYPO3 \CMS \Core \Domain \RecordInterface::class), 'RecordInterface should exist in v14+ ' );
258+
259+ // Test that RecordFactory can create records
260+ $ recordFactory = GeneralUtility::makeInstance (\TYPO3 \CMS \Core \Domain \RecordFactory::class);
261+ $ testData = ['uid ' => 1 , 'pid ' => 1 , 'CType ' => 'shortcut ' ];
262+ $ record = $ recordFactory ->createFromDatabaseRow ('tt_content ' , $ testData );
263+
264+ self ::assertInstanceOf (\TYPO3 \CMS \Core \Domain \RecordInterface::class, $ record );
265+ self ::assertTrue (method_exists ($ record , 'getRow ' ), 'RecordInterface should have getRow() method in v14+ ' );
266+
267+ } else {
268+ // Test TYPO3 v13 compatibility
269+ self ::assertLessThan (14 , $ majorVersion , 'This should be TYPO3 v13 or below ' );
270+
271+ // In v13, records might be handled differently
272+ // The renderer should still work but use different internal methods
273+ }
274+
275+ // Test that GridColumnItem class exists (used by the renderer)
276+ self ::assertTrue (class_exists (\TYPO3 \CMS \Backend \View \BackendLayout \Grid \GridColumnItem::class));
277+ }
278+ }
0 commit comments