1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | Object.defineProperty(exports, "__esModule", { value: true });
|
13 | const chai_1 = require("chai");
|
14 | const shady_css_1 = require("../shady-css");
|
15 | const ast_iterator_1 = require("../shady-css/ast-iterator");
|
16 | const fixtures = require("./fixtures");
|
17 | const test_node_factory_1 = require("./test-node-factory");
|
18 | chai_1.use(require('chai-subset'));
|
19 | describe('Parser', () => {
|
20 | let parser;
|
21 | let nodeFactory;
|
22 | beforeEach(() => {
|
23 | parser = new shady_css_1.Parser();
|
24 | nodeFactory = new test_node_factory_1.TestNodeFactory();
|
25 | });
|
26 | describe('when parsing css', () => {
|
27 | it('can parse a basic ruleset', () => {
|
28 | chai_1.expect(parser.parse(fixtures.basicRuleset))
|
29 | .to.be.containSubset(nodeFactory.stylesheet([
|
30 | nodeFactory.ruleset('body', nodeFactory.rulelist([
|
31 | nodeFactory.declaration('margin', nodeFactory.expression('0')),
|
32 | nodeFactory.declaration('padding', nodeFactory.expression('0px'))
|
33 | ]))
|
34 | ]));
|
35 | });
|
36 | it('can parse at rules', () => {
|
37 | chai_1.expect(parser.parse(fixtures.atRules))
|
38 | .to.containSubset(nodeFactory.stylesheet([
|
39 | nodeFactory.atRule('import', 'url(\'foo.css\')', undefined),
|
40 | nodeFactory.atRule('font-face', '', nodeFactory.rulelist([nodeFactory.declaration('font-family', nodeFactory.expression('foo'))])),
|
41 | nodeFactory.atRule('charset', '\'foo\'', undefined)
|
42 | ]));
|
43 | });
|
44 | it('can parse keyframes', () => {
|
45 | chai_1.expect(parser.parse(fixtures.keyframes))
|
46 | .to.containSubset(nodeFactory.stylesheet([
|
47 | nodeFactory.atRule('keyframes', 'foo', nodeFactory.rulelist([
|
48 | nodeFactory.ruleset('from', nodeFactory.rulelist([nodeFactory.declaration('fiz', nodeFactory.expression('0%'))])),
|
49 | nodeFactory.ruleset('99.9%', nodeFactory.rulelist([
|
50 | nodeFactory.declaration('fiz', nodeFactory.expression('100px')),
|
51 | nodeFactory.declaration('buz', nodeFactory.expression('true'))
|
52 | ]))
|
53 | ]))
|
54 | ]));
|
55 | });
|
56 | it('can parse custom properties', () => {
|
57 | chai_1.expect(parser.parse(fixtures.customProperties))
|
58 | .to.containSubset(nodeFactory.stylesheet([nodeFactory.ruleset(':root', nodeFactory.rulelist([
|
59 | nodeFactory.declaration('--qux', nodeFactory.expression('vim')),
|
60 | nodeFactory.declaration('--foo', nodeFactory.rulelist([nodeFactory.declaration('bar', nodeFactory.expression('baz'))]))
|
61 | ]))]));
|
62 | });
|
63 | it('can parse declarations with no value', () => {
|
64 | chai_1.expect(parser.parse(fixtures.declarationsWithNoValue))
|
65 | .to.containSubset(nodeFactory.stylesheet([
|
66 | nodeFactory.declaration('foo', undefined),
|
67 | nodeFactory.declaration('bar 20px', undefined),
|
68 | nodeFactory.ruleset('div', nodeFactory.rulelist([nodeFactory.declaration('baz', undefined)]))
|
69 | ]));
|
70 | });
|
71 | it('can parse minified rulelists', () => {
|
72 | chai_1.expect(parser.parse(fixtures.minifiedRuleset))
|
73 | .to.containSubset(nodeFactory.stylesheet([
|
74 | nodeFactory.ruleset('.foo', nodeFactory.rulelist([nodeFactory.declaration('bar', nodeFactory.expression('baz'))])),
|
75 | nodeFactory.ruleset('div .qux', nodeFactory.rulelist([nodeFactory.declaration('vim', nodeFactory.expression('fet'))]))
|
76 | ]));
|
77 | });
|
78 | it('can parse psuedo rulesets', () => {
|
79 | chai_1.expect(parser.parse(fixtures.psuedoRuleset))
|
80 | .to.containSubset(nodeFactory.stylesheet([nodeFactory.ruleset('.foo:bar:not(#rif)', nodeFactory.rulelist([nodeFactory.declaration('baz', nodeFactory.expression('qux'))]))]));
|
81 | });
|
82 | it('can parse rulelists with data URIs', () => {
|
83 | chai_1.expect(parser.parse(fixtures.dataUriRuleset))
|
84 | .to.containSubset(nodeFactory.stylesheet([nodeFactory.ruleset('.foo', nodeFactory.rulelist([nodeFactory.declaration('bar', nodeFactory.expression('url(qux;gib)'))]))]));
|
85 | });
|
86 | it('can parse pathological comments', () => {
|
87 | chai_1.expect(parser.parse(fixtures.pathologicalComments))
|
88 | .to.containSubset(nodeFactory.stylesheet([
|
89 | nodeFactory.ruleset('.foo', nodeFactory.rulelist([nodeFactory.declaration('bar', nodeFactory.expression('/*baz*/vim'))])),
|
90 | nodeFactory.comment('/* unclosed\n@fiz {\n --huk: {\n /* buz */'),
|
91 | nodeFactory.declaration('baz', nodeFactory.expression('lur')),
|
92 | nodeFactory.discarded('};'),
|
93 | nodeFactory.discarded('}'),
|
94 | nodeFactory.atRule('gak', 'wiz', undefined)
|
95 | ]));
|
96 | });
|
97 | it('disregards extra semi-colons', () => {
|
98 | chai_1.expect(parser.parse(fixtures.extraSemicolons))
|
99 | .to.containSubset(nodeFactory.stylesheet([
|
100 | nodeFactory.ruleset(':host', nodeFactory.rulelist([
|
101 | nodeFactory.declaration('margin', nodeFactory.expression('0')),
|
102 | nodeFactory.discarded(';;'),
|
103 | nodeFactory.declaration('padding', nodeFactory.expression('0')),
|
104 | nodeFactory.discarded(';'),
|
105 | nodeFactory.discarded(';'),
|
106 | nodeFactory.declaration('display', nodeFactory.expression('block')),
|
107 | ])),
|
108 | nodeFactory.discarded(';')
|
109 | ]));
|
110 | });
|
111 | });
|
112 | describe('when extracting ranges', () => {
|
113 | it('extracts the correct ranges for discarded nodes', () => {
|
114 | const ast = parser.parse(fixtures.extraSemicolons);
|
115 | const nodes = getNodesOfType(ast, 'discarded');
|
116 | const rangeSubStrings = Array.from(nodes).map((n) => {
|
117 | return fixtures.extraSemicolons.substring(n.range.start, n.range.end);
|
118 | });
|
119 | chai_1.expect(rangeSubStrings).to.be.deep.equal([';;', ';', ';', ';']);
|
120 | });
|
121 | it('extracts the correct ranges for expression nodes', () => {
|
122 | const ast = parser.parse(fixtures.basicRuleset);
|
123 | const nodes = getNodesOfType(ast, 'expression');
|
124 | const rangeSubStrings = Array.from(nodes).map((n) => {
|
125 | return fixtures.basicRuleset.substring(n.range.start, n.range.end);
|
126 | });
|
127 | chai_1.expect(rangeSubStrings).to.be.deep.equal(['0', '0px']);
|
128 | });
|
129 | it('extracts the correct ranges for declarations', () => {
|
130 | const expectDeclarationRanges = (cssText, expectedRangeStrings, expectedNameRangeStrings) => {
|
131 | const ast = parser.parse(cssText);
|
132 | const nodes = Array.from(getNodesOfType(ast, 'declaration'));
|
133 | const rangeStrings = nodes.map((n) => {
|
134 | return cssText.substring(n.range.start, n.range.end);
|
135 | });
|
136 | const nameRangeStrings = nodes.map((n) => {
|
137 | return cssText.substring(n.nameRange.start, n.nameRange.end);
|
138 | });
|
139 | chai_1.expect(rangeStrings).to.be.deep.equal(expectedRangeStrings);
|
140 | chai_1.expect(nameRangeStrings).to.be.deep.equal(expectedNameRangeStrings);
|
141 | };
|
142 | expectDeclarationRanges(fixtures.basicRuleset, ['margin: 0;', 'padding: 0px'], ['margin', 'padding']);
|
143 | expectDeclarationRanges(fixtures.atRules, ['font-family: foo;'], ['font-family']);
|
144 | expectDeclarationRanges(fixtures.keyframes, ['fiz: 0%;', 'fiz: 100px;', 'buz: true;'], ['fiz', 'fiz', 'buz']);
|
145 | expectDeclarationRanges(fixtures.customProperties, ['--qux: vim;', '--foo: {\n bar: baz;\n };', 'bar: baz;'], ['--qux', '--foo', 'bar']);
|
146 | expectDeclarationRanges(fixtures.extraSemicolons, [
|
147 | 'margin: 0;',
|
148 | 'padding: 0;',
|
149 | 'display: block;',
|
150 | ], ['margin', 'padding', 'display']);
|
151 | expectDeclarationRanges(fixtures.declarationsWithNoValue, ['foo;', 'bar 20px;', 'baz;'], ['foo', 'bar 20px', 'baz']);
|
152 | expectDeclarationRanges(fixtures.minifiedRuleset, ['bar:baz', 'vim:fet;'], ['bar', 'vim']);
|
153 | expectDeclarationRanges(fixtures.psuedoRuleset, ['baz:qux'], ['baz']);
|
154 | expectDeclarationRanges(fixtures.dataUriRuleset, ['bar:url(qux;gib)'], ['bar']);
|
155 | expectDeclarationRanges(fixtures.pathologicalComments, ['bar: /*baz*/vim;', 'baz: lur;'], ['bar', 'baz']);
|
156 | });
|
157 | it('extracts the correct ranges for rulesets', () => {
|
158 | const expectRulesetRanges = (cssText, expectedRangeStrings, expectedSelectorRangeStrings) => {
|
159 | const ast = parser.parse(cssText);
|
160 | const nodes = Array.from(getNodesOfType(ast, 'ruleset'));
|
161 | const rangeStrings = nodes.map((n) => {
|
162 | return cssText.substring(n.range.start, n.range.end);
|
163 | });
|
164 | const selectorRangeStrings = nodes.map((n) => {
|
165 | return cssText.substring(n.selectorRange.start, n.selectorRange.end);
|
166 | });
|
167 | chai_1.expect(rangeStrings).to.be.deep.equal(expectedRangeStrings);
|
168 | chai_1.expect(selectorRangeStrings)
|
169 | .to.be.deep.equal(expectedSelectorRangeStrings);
|
170 | };
|
171 | expectRulesetRanges(fixtures.basicRuleset, [`body {\n margin: 0;\n padding: 0px\n}`], ['body']);
|
172 | expectRulesetRanges(fixtures.atRules, [], []);
|
173 | expectRulesetRanges(fixtures.keyframes, [
|
174 | 'from {\n fiz: 0%;\n }',
|
175 | '99.9% {\n fiz: 100px;\n buz: true;\n }'
|
176 | ], ['from', '99.9%']);
|
177 | expectRulesetRanges(fixtures.customProperties, [':root {\n --qux: vim;\n --foo: {\n bar: baz;\n };\n}'], [':root']);
|
178 | expectRulesetRanges(fixtures.extraSemicolons, [':host {\n margin: 0;;;\n padding: 0;;\n ;display: block;\n}'], [':host']);
|
179 | expectRulesetRanges(fixtures.declarationsWithNoValue, ['div {\n baz;\n}'], ['div']);
|
180 | expectRulesetRanges(fixtures.minifiedRuleset, ['.foo{bar:baz}', 'div .qux{vim:fet;}'], ['.foo', 'div .qux']);
|
181 | expectRulesetRanges(fixtures.psuedoRuleset, ['.foo:bar:not(#rif){baz:qux}'], ['.foo:bar:not(#rif)']);
|
182 | expectRulesetRanges(fixtures.dataUriRuleset, ['.foo{bar:url(qux;gib)}'], ['.foo']);
|
183 | expectRulesetRanges(fixtures.pathologicalComments, ['.foo {\n bar: /*baz*/vim;\n}'], ['.foo']);
|
184 | });
|
185 | it('extracts the correct ranges for rulelists', () => {
|
186 | const expectRulelistRanges = (cssText, expectedRangeStrings) => {
|
187 | const ast = parser.parse(cssText);
|
188 | const nodes = Array.from(getNodesOfType(ast, 'rulelist'));
|
189 | const rangeStrings = nodes.map((n) => {
|
190 | return cssText.substring(n.range.start, n.range.end);
|
191 | });
|
192 | chai_1.expect(rangeStrings).to.be.deep.equal(expectedRangeStrings);
|
193 | };
|
194 | expectRulelistRanges(fixtures.basicRuleset, [`{\n margin: 0;\n padding: 0px\n}`]);
|
195 | expectRulelistRanges(fixtures.atRules, ['{\n font-family: foo;\n}']);
|
196 | expectRulelistRanges(fixtures.keyframes, [
|
197 | '{\n from {\n fiz: 0%;\n }\n\n 99.9% ' +
|
198 | '{\n fiz: 100px;\n buz: true;\n }\n}',
|
199 | '{\n fiz: 0%;\n }',
|
200 | '{\n fiz: 100px;\n buz: true;\n }'
|
201 | ]);
|
202 | expectRulelistRanges(fixtures.customProperties, [
|
203 | '{\n --qux: vim;\n --foo: {\n bar: baz;\n };\n}',
|
204 | '{\n bar: baz;\n }'
|
205 | ]);
|
206 | expectRulelistRanges(fixtures.extraSemicolons, ['{\n margin: 0;;;\n padding: 0;;\n ;display: block;\n}']);
|
207 | expectRulelistRanges(fixtures.declarationsWithNoValue, ['{\n baz;\n}']);
|
208 | expectRulelistRanges(fixtures.minifiedRuleset, ['{bar:baz}', '{vim:fet;}']);
|
209 | expectRulelistRanges(fixtures.psuedoRuleset, ['{baz:qux}']);
|
210 | expectRulelistRanges(fixtures.dataUriRuleset, ['{bar:url(qux;gib)}']);
|
211 | expectRulelistRanges(fixtures.pathologicalComments, ['{\n bar: /*baz*/vim;\n}']);
|
212 | });
|
213 | it('extracts the correct ranges for atRules', () => {
|
214 | const expectAtRuleRanges = (cssText, expectedRangeStrings, expectedNameRangeStrings, expectedParameterRangeStrings) => {
|
215 | const ast = parser.parse(cssText);
|
216 | const nodes = Array.from(getNodesOfType(ast, 'atRule'));
|
217 | const rangeStrings = nodes.map((n) => {
|
218 | return cssText.substring(n.range.start, n.range.end);
|
219 | });
|
220 | const rangeNameStrings = nodes.map((n) => {
|
221 | return cssText.substring(n.nameRange.start, n.nameRange.end);
|
222 | });
|
223 | const rangeParametersStrings = nodes.map((n) => {
|
224 | if (!n.parametersRange) {
|
225 | return '[no parameters]';
|
226 | }
|
227 | return cssText.substring(n.parametersRange.start, n.parametersRange.end);
|
228 | });
|
229 | chai_1.expect(rangeStrings).to.be.deep.equal(expectedRangeStrings);
|
230 | chai_1.expect(rangeNameStrings).to.be.deep.equal(expectedNameRangeStrings);
|
231 | chai_1.expect(rangeParametersStrings)
|
232 | .to.be.deep.equal(expectedParameterRangeStrings);
|
233 | };
|
234 | expectAtRuleRanges(fixtures.basicRuleset, [], [], []);
|
235 | expectAtRuleRanges(fixtures.atRules, [
|
236 | `@import url('foo.css');`,
|
237 | `@font-face {\n font-family: foo;\n}`,
|
238 | `@charset 'foo';`
|
239 | ], ['import', 'font-face', 'charset'], [`url('foo.css')`, `[no parameters]`, `'foo'`]);
|
240 | expectAtRuleRanges(fixtures.keyframes, [
|
241 | '@keyframes foo {\n from {\n fiz: 0%;\n }\n\n 99.9% ' +
|
242 | '{\n fiz: 100px;\n buz: true;\n }\n}',
|
243 | ], ['keyframes'], ['foo']);
|
244 | expectAtRuleRanges(fixtures.customProperties, [], [], []);
|
245 | expectAtRuleRanges(fixtures.extraSemicolons, [], [], []);
|
246 | expectAtRuleRanges(fixtures.declarationsWithNoValue, [], [], []);
|
247 | expectAtRuleRanges(fixtures.minifiedRuleset, [], [], []);
|
248 | expectAtRuleRanges(fixtures.psuedoRuleset, [], [], []);
|
249 | expectAtRuleRanges(fixtures.dataUriRuleset, [], [], []);
|
250 | expectAtRuleRanges(fixtures.pathologicalComments, ['@gak wiz;'], ['gak'], ['wiz']);
|
251 | });
|
252 | });
|
253 | });
|
254 | function* getNodesOfType(node, type) {
|
255 | for (const n of ast_iterator_1.iterateOverAst(node)) {
|
256 | if (n.type === type) {
|
257 | yield n;
|
258 | }
|
259 | }
|
260 | }
|
261 |
|
\ | No newline at end of file |