@@ -51,6 +51,24 @@ public static function provideSyntacticallyCorrectAtRule(): array
5151 'container ' => [
5252 '@container (min-width: 60rem) { .items { background: blue; } } ' ,
5353 ],
54+ 'layer named ' => [
55+ '@layer theme { .button { color: blue; } } ' ,
56+ ],
57+ 'layer anonymous ' => [
58+ '@layer { .card { padding: 1rem; } } ' ,
59+ ],
60+ 'scope with selector ' => [
61+ '@scope (.card) { .title { font-size: 2rem; } } ' ,
62+ ],
63+ 'scope root only ' => [
64+ '@scope { .content { margin: 0; } } ' ,
65+ ],
66+ 'scope with limit ' => [
67+ '@scope (.article-body) to (figure) { h2 { color: red; } } ' ,
68+ ],
69+ 'starting-style ' => [
70+ '@starting-style { .dialog { opacity: 0; transform: translateY(-10px); } } ' ,
71+ ],
5472 ];
5573 }
5674
@@ -94,4 +112,110 @@ public function parsesSyntacticallyCorrectAtRuleInStrictMode(string $css): void
94112
95113 self ::assertNotEmpty ($ contents , 'Failing CSS: ` ' . $ css . '` ' );
96114 }
115+
116+ /**
117+ * @return array<string, array{0: string, 1: string, 2: string, 3: int}>
118+ */
119+ public static function provideAtRuleParsingData (): array
120+ {
121+ return [
122+ 'layer with named argument ' => [
123+ '@layer theme { .button { color: blue; } } ' ,
124+ 'layer ' ,
125+ 'theme ' ,
126+ 1 ,
127+ ],
128+ 'layer without arguments ' => [
129+ '@layer { .card { padding: 1rem; } } ' ,
130+ 'layer ' ,
131+ '' ,
132+ 1 ,
133+ ],
134+ 'scope with selector ' => [
135+ '@scope (.card) { .title { font-size: 2rem; } } ' ,
136+ 'scope ' ,
137+ '(.card) ' ,
138+ 1 ,
139+ ],
140+ 'scope without selector ' => [
141+ '@scope { .content { margin: 0; } } ' ,
142+ 'scope ' ,
143+ '' ,
144+ 1 ,
145+ ],
146+ 'scope with limit ' => [
147+ '@scope (.article-body) to (figure) { h2 { color: red; } } ' ,
148+ 'scope ' ,
149+ '(.article-body) to (figure) ' ,
150+ 1 ,
151+ ],
152+ 'starting-style ' => [
153+ '@starting-style { .dialog { opacity: 0; transform: translateY(-10px); } } ' ,
154+ 'starting-style ' ,
155+ '' ,
156+ 1 ,
157+ ],
158+ ];
159+ }
160+
161+ /**
162+ * @return array<string, array{0: string, 1: list<string>}>
163+ */
164+ public static function provideAtRuleRenderingData (): array
165+ {
166+ return [
167+ 'layer with named argument ' => [
168+ '@layer theme { .button { color: blue; } } ' ,
169+ ['@layer theme ' , '.button ' , 'color: blue ' ],
170+ ],
171+ 'scope with selector ' => [
172+ '@scope (.card) { .title { font-size: 2rem; } } ' ,
173+ ['@scope (.card) ' , '.title ' , 'font-size: 2rem ' ],
174+ ],
175+ 'scope with limit ' => [
176+ '@scope (.article-body) to (figure) { h2 { color: red; } } ' ,
177+ ['@scope (.article-body) to (figure) ' , 'h2 ' , 'color: red ' ],
178+ ],
179+ 'starting-style ' => [
180+ '@starting-style { .dialog { opacity: 0; } } ' ,
181+ ['@starting-style ' , '.dialog ' , 'opacity: 0 ' ],
182+ ],
183+ ];
184+ }
185+
186+ /**
187+ * @test
188+ *
189+ * @dataProvider provideAtRuleParsingData
190+ */
191+ public function parsesAtRuleBlockList (
192+ string $ css ,
193+ string $ expectedName ,
194+ string $ expectedArgs ,
195+ int $ expectedContentCount
196+ ): void {
197+ $ contents = (new Parser ($ css ))->parse ()->getContents ();
198+ $ atRuleBlockList = $ contents [0 ];
199+
200+ self ::assertInstanceOf (AtRuleBlockList::class, $ atRuleBlockList );
201+ self ::assertSame ($ expectedName , $ atRuleBlockList ->atRuleName ());
202+ self ::assertSame ($ expectedArgs , $ atRuleBlockList ->atRuleArgs ());
203+ self ::assertCount ($ expectedContentCount , $ atRuleBlockList ->getContents ());
204+ }
205+
206+ /**
207+ * @test
208+ *
209+ * @dataProvider provideAtRuleRenderingData
210+ *
211+ * @param list<string> $expectedSubstrings
212+ */
213+ public function rendersAtRuleBlockListCorrectly (string $ css , array $ expectedSubstrings ): void
214+ {
215+ $ rendered = (new Parser ($ css ))->parse ()->render ();
216+
217+ foreach ($ expectedSubstrings as $ expected ) {
218+ self ::assertStringContainsString ($ expected , $ rendered );
219+ }
220+ }
97221}
0 commit comments