77
88namespace Magento \Framework \Css \Test \Unit \PreProcessor \Adapter \Less ;
99
10+ use Magento \Framework \App \Filesystem \DirectoryList ;
1011use Magento \Framework \App \State ;
1112use Magento \Framework \Css \PreProcessor \Adapter \Less \Processor ;
1213use Magento \Framework \Css \PreProcessor \File \Temporary ;
@@ -23,8 +24,8 @@ class ProcessorTest extends TestCase
2324 const ASSET_PATH = 'test-path ' ;
2425
2526 const TMP_PATH_LESS = '_file/test.less ' ;
26-
27- const TMP_PATH_CSS = '_file/test.css ' ;
27+ const TMP_PATH_CSS_PRODUCTION = ' _file/test-production.css ' ;
28+ const TMP_PATH_CSS_DEVELOPER = '_file/test-developer .css ' ;
2829
2930 const ERROR_MESSAGE = 'Test exception ' ;
3031
@@ -52,6 +53,10 @@ class ProcessorTest extends TestCase
5253 * @var Temporary|MockObject
5354 */
5455 private $ temporaryFileMock ;
56+ /**
57+ * @var DirectoryList|MockObject
58+ */
59+ private $ directoryListMock ;
5560
5661 /**
5762 * Set up
@@ -69,12 +74,16 @@ protected function setUp(): void
6974 $ this ->temporaryFileMock = $ this ->getMockBuilder (Temporary::class)
7075 ->disableOriginalConstructor ()
7176 ->getMock ();
77+ $ this ->directoryListMock = $ this ->getMockBuilder (DirectoryList::class)
78+ ->disableOriginalConstructor ()
79+ ->getMock ();
7280
7381 $ this ->processor = new Processor (
7482 $ this ->loggerMock ,
7583 $ this ->appStateMock ,
7684 $ this ->assetSourceMock ,
77- $ this ->temporaryFileMock
85+ $ this ->temporaryFileMock ,
86+ $ this ->directoryListMock ,
7887 );
7988 }
8089
@@ -89,7 +98,7 @@ public function testProcessContentException()
8998
9099 $ this ->appStateMock ->expects (self ::once ())
91100 ->method ('getMode ' )
92- ->willReturn (State::MODE_DEVELOPER );
101+ ->willReturn (State::MODE_PRODUCTION );
93102
94103 $ this ->assetSourceMock ->expects (self ::once ())
95104 ->method ('getContent ' )
@@ -120,7 +129,7 @@ public function testProcessContentEmpty()
120129
121130 $ this ->appStateMock ->expects (self ::once ())
122131 ->method ('getMode ' )
123- ->willReturn (State::MODE_DEVELOPER );
132+ ->willReturn (State::MODE_PRODUCTION );
124133
125134 $ this ->assetSourceMock ->expects (self ::once ())
126135 ->method ('getContent ' )
@@ -141,15 +150,15 @@ public function testProcessContentEmpty()
141150 }
142151
143152 /**
144- * Test for processContent method (not empty content)
153+ * Test for processContent method in production mode (not empty content)
145154 */
146155 public function testProcessContentNotEmpty ()
147156 {
148157 $ assetMock = $ this ->getAssetMock ();
149158
150159 $ this ->appStateMock ->expects (self ::once ())
151160 ->method ('getMode ' )
152- ->willReturn (State::MODE_DEVELOPER );
161+ ->willReturn (State::MODE_PRODUCTION );
153162
154163 $ this ->assetSourceMock ->expects (self ::once ())
155164 ->method ('getContent ' )
@@ -170,11 +179,46 @@ public function testProcessContentNotEmpty()
170179
171180 $ clearSymbol = ["\n" , "\r" , "\t" , ' ' ];
172181 self ::assertEquals (
173- trim (str_replace ($ clearSymbol , '' , file_get_contents (__DIR__ . '/ ' . self ::TMP_PATH_CSS ))),
182+ trim (str_replace ($ clearSymbol , '' , file_get_contents (__DIR__ . '/ ' . self ::TMP_PATH_CSS_PRODUCTION ))),
174183 trim (str_replace ($ clearSymbol , '' , $ this ->processor ->processContent ($ assetMock )))
175184 );
176185 }
177186
187+ /**
188+ * Test for processContent method in developer mode (not empty content)
189+ */
190+ public function testProcessContentNotEmptyInDeveloperMode ()
191+ {
192+ $ assetMock = $ this ->getAssetMock ();
193+
194+ $ this ->appStateMock ->expects (self ::once ())
195+ ->method ('getMode ' )
196+ ->willReturn (State::MODE_DEVELOPER );
197+
198+ $ this ->assetSourceMock ->expects (self ::once ())
199+ ->method ('getContent ' )
200+ ->with ($ assetMock )
201+ ->willReturn (self ::TEST_CONTENT );
202+
203+ $ this ->temporaryFileMock ->expects (self ::once ())
204+ ->method ('createFile ' )
205+ ->with (self ::ASSET_PATH , self ::TEST_CONTENT )
206+ ->willReturn (__DIR__ . '/ ' . self ::TMP_PATH_LESS );
207+
208+ $ assetMock ->expects (self ::once ())
209+ ->method ('getPath ' )
210+ ->willReturn (self ::ASSET_PATH );
211+
212+ $ this ->loggerMock ->expects (self ::never ())
213+ ->method ('critical ' );
214+
215+ $ clearSymbol = ["\n" , "\r" , "\t" , ' ' ];
216+ self ::assertEquals (
217+ trim (str_replace ($ clearSymbol , '' , file_get_contents (__DIR__ . '/ ' . self ::TMP_PATH_CSS_DEVELOPER ))),
218+ trim (str_replace ($ clearSymbol , '' , $ this ->normalizeInlineSourceMap ($ this ->processor ->processContent ($ assetMock ))))
219+ );
220+ }
221+
178222 /**
179223 * @return File|MockObject
180224 */
@@ -186,4 +230,34 @@ private function getAssetMock()
186230
187231 return $ assetMock ;
188232 }
233+
234+ /**
235+ * - find json part of sourcemap
236+ * - url decode it
237+ * - replace \/ with / in source filenames
238+ * - remove absolute path in filename, make it a relative path
239+ */
240+ private function normalizeInlineSourceMap (string $ css ): string
241+ {
242+ $ regexBegin = 'sourceMappingURL=data:application/json, ' ;
243+ $ regexEnd = '*/ ' ;
244+ $ regex = '@ ' . preg_quote ($ regexBegin , '@ ' ) . '([^\*]+) ' . preg_quote ($ regexEnd , '@ ' ) . '@ ' ;
245+
246+ if (preg_match ($ regex , $ css , $ matches ) === 1 ) {
247+ $ inlineSourceMapJson = $ matches [1 ];
248+ $ inlineSourceMapJson = urldecode ($ inlineSourceMapJson );
249+ $ inlineSourceMapJson = json_decode ($ inlineSourceMapJson , true , 512 , JSON_UNESCAPED_SLASHES );
250+
251+ $ relativeFilenames = [];
252+ foreach ($ inlineSourceMapJson ['sources ' ] as $ filename ) {
253+ $ relativeFilenames [] = str_replace (sprintf ('%s/ ' , BP ), '' , $ filename );
254+ }
255+ $ inlineSourceMapJson ['sources ' ] = $ relativeFilenames ;
256+ $ inlineSourceMapJson = json_encode ($ inlineSourceMapJson , JSON_UNESCAPED_SLASHES );
257+
258+ $ css = preg_replace ($ regex , sprintf ('%s%s%s ' , $ regexBegin , $ inlineSourceMapJson , $ regexEnd ), $ css );
259+ }
260+
261+ return $ css ;
262+ }
189263}
0 commit comments