UNPKG

sass-true

Version:

Unit testing for Sass.

665 lines 30.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = exports.formatFailureMessage = exports.runSass = void 0; const node_assert_1 = __importDefault(require("node:assert")); const node_path_1 = __importDefault(require("node:path")); const jest_diff_1 = require("jest-diff"); const postcss_1 = require("postcss"); const constants = __importStar(require("./constants")); const utils_1 = require("./utils"); const loadSass = function (sassPkg) { try { // eslint-disable-next-line @typescript-eslint/no-require-imports return require(sassPkg); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (err) { throw new Error(`Cannot find Dart Sass (\`${sassPkg}\`) dependency.`); } }; const runSass = function (trueOptions, src, // eslint-disable-next-line @typescript-eslint/no-explicit-any sassOptions) { const trueOpts = Object.assign({}, trueOptions); const sassOpts = Object.assign({}, sassOptions); // Add True's sass to `loadPaths` const sassPath = node_path_1.default.join(__dirname, '..', 'sass'); if (sassOpts.loadPaths) { sassOpts.loadPaths.push(sassPath); } else { sassOpts.loadPaths = [sassPath]; } // Error if arguments match v6 API if (typeof src !== 'string' || !trueOptions.describe || !trueOptions.it) { throw new Error('The arguments provided to `runSass` do not match the new API ' + 'introduced in True v7. Refer to the v7 release notes ' + 'for migration documentation: ' + 'https://github.com/oddbird/true/releases/tag/v7.0.0'); } // Error if `style: "compressed"` is used if (sassOpts.style === 'compressed') { throw new Error('True requires the default Sass `expanded` output style, ' + 'but `style: "compressed"` was used.'); } let compiler; if (trueOpts.sass && typeof trueOpts.sass !== 'string') { compiler = trueOpts.sass; } else if (typeof trueOpts.sass === 'string') { compiler = loadSass(trueOpts.sass); } else { try { // try sass-embedded before sass compiler = loadSass('sass-embedded'); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e1) { /* v8 ignore next */ try { compiler = loadSass('sass'); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e2) { throw new Error('Cannot find Dart Sass (`sass-embedded` or `sass`) dependency.'); } } } // Add the Sass Node.js package importer, if available if (!sassOpts.importers && compiler.NodePackageImporter) { sassOpts.importers = [new compiler.NodePackageImporter()]; } const compilerFn = trueOpts.sourceType === 'string' ? 'compileString' : 'compile'; const parsedCss = compiler[compilerFn](src, sassOpts).css; const modules = (0, exports.parse)(parsedCss, trueOpts.contextLines); modules.forEach((module) => { describeModule(module, trueOpts.describe, trueOpts.it); }); }; exports.runSass = runSass; const formatFailureMessage = function (assertion) { let msg = `${assertion.description} `; msg = `${msg}[type: ${assertion.assertionType}]`; if (assertion.details) { msg = `${msg} -- ${assertion.details}`; } // For contains-string assertions with multiple strings, show which ones are missing if (assertion.assertionType === 'contains-string' && assertion.expected) { const expectedStrings = assertion.expected.split('\n'); /* v8 ignore else */ if (expectedStrings.length > 1) { const output = assertion.output || ''; const missing = expectedStrings.filter((str) => !output.includes(str)); /* v8 ignore else */ if (missing.length > 0) { msg = `${msg}\n\nExpected output to contain all of the following strings:\n`; expectedStrings.forEach((str) => { const found = output.includes(str); msg = `${msg} ${found ? '✓' : '✗'} "${str}"\n`; }); msg = `${msg}\nActual output:\n${output}\n`; return msg; } } } // For contains assertions with multiple blocks, show which ones are missing if (assertion.assertionType === 'contains' && assertion.expected) { const expectedBlocks = assertion.expected.split('\n---\n'); /* v8 ignore else */ if (expectedBlocks.length > 1) { const output = assertion.output || ''; const missing = expectedBlocks.filter((block) => !contains(output, block)); /* v8 ignore else */ if (missing.length > 0) { msg = `${msg}\n\nExpected output to contain all of the following CSS blocks:\n`; expectedBlocks.forEach((block, index) => { const found = contains(output, block); msg = `${msg} ${found ? '✓' : '✗'} Block ${index + 1}:\n`; msg = `${msg}${block .split('\n') .map((line) => ` ${line}`) .join('\n')}\n`; }); msg = `${msg}\nActual output:\n${output}\n`; return msg; } } } msg = `${msg}\n\n${(0, jest_diff_1.diffStringsUnified)(assertion.expected || '', assertion.output || '')}\n`; return msg; }; exports.formatFailureMessage = formatFailureMessage; const describeModule = function (module, describe, it) { describe(module.module, () => { var _a, _b; (_a = module.modules) === null || _a === void 0 ? void 0 : _a.forEach((submodule) => { describeModule(submodule, describe, it); }); (_b = module.tests) === null || _b === void 0 ? void 0 : _b.forEach((test) => { it(test.test, () => { var _a; (_a = test.assertions) === null || _a === void 0 ? void 0 : _a.forEach((assertion) => { if (!assertion.passed) { node_assert_1.default.fail((0, exports.formatFailureMessage)(assertion)); } }); }); }); }); }; const finishCurrentModule = function (ctx) { finishCurrentTest(ctx); if (ctx.currentModule) { const paths = ctx.currentModule.module.split(constants.MODULE_NESTING_TOKEN); ctx.currentModule.module = paths[paths.length - 1] || ''; insertModule(paths, ctx.currentModule, ctx); delete ctx.currentModule; } }; const finishCurrentTest = function (ctx) { var _a, _b; finishCurrentAssertion(ctx); if (ctx.currentTest) { (_b = (_a = ctx.currentModule) === null || _a === void 0 ? void 0 : _a.tests) === null || _b === void 0 ? void 0 : _b.push(ctx.currentTest); delete ctx.currentTest; } }; const finishCurrentAssertion = function (ctx) { var _a; if (ctx.currentAssertion) { (_a = ctx.currentTest) === null || _a === void 0 ? void 0 : _a.assertions.push(ctx.currentAssertion); delete ctx.currentAssertion; } }; const insertModule = function (paths, module, ctx) { if (!ctx.modules) { ctx.modules = []; } if (paths.length > 1) { let newCtx = ctx.modules.find((submod) => submod.module === paths[0]); if (!newCtx) { newCtx = { module: paths[0] }; ctx.modules.push(newCtx); } insertModule(paths.slice(1), module, newCtx); } else { ctx.modules.push(module); } }; const dealWithAnnoyingMediaQueries = function (rawCSS) { const matchMediaQuery = /(@[a-zA-Z0-9:()\s-]+)/g; const matchCSSWithinMediaQueryBlock = /@[a-zA-Z0-9:()\s-]+{([a-zA-Z0-9:()\s-;._\\n{}]+)(?!}\\n})/g; const mediaqueries = rawCSS.match(matchMediaQuery); const rawCSSSansMediaQueries = rawCSS .replace(matchMediaQuery, '') .replace(matchCSSWithinMediaQueryBlock, '') .replace(/^{/, ''); let matches = matchCSSWithinMediaQueryBlock.exec(rawCSS); let i = 0; let mediaQueryBasedSelectors = []; const mediaqueryRule = (rule) => ((mediaqueries === null || mediaqueries === void 0 ? void 0 : mediaqueries[i]) || '') + rule; while (matches !== null) { // This is necessary to avoid infinite loops with zero-width matches /* v8 ignore next */ if (matches.index === matchCSSWithinMediaQueryBlock.lastIndex) { matchCSSWithinMediaQueryBlock.lastIndex++; } const cssWithinMediaQuery = (0, utils_1.removeNewLines)(matches[1]); const cssRules = (0, utils_1.cssStringToArrayOfRules)(cssWithinMediaQuery); mediaQueryBasedSelectors = mediaQueryBasedSelectors.concat(cssRules.map(mediaqueryRule)); i++; matches = matchCSSWithinMediaQueryBlock.exec(rawCSS); } return { mediaQueryBasedSelectors, rawCSSSansMediaQueries, }; }; const createSelectorsRulesPairs = function (cssString) { const processedMediaQueries = dealWithAnnoyingMediaQueries(cssString); const mediaQueries = (0, utils_1.splitSelectorAndProperties)(processedMediaQueries.mediaQueryBasedSelectors); const nonMediaQueries = processedMediaQueries.rawCSSSansMediaQueries; const blocks = (0, utils_1.cssStringToArrayOfRules)(nonMediaQueries); const splitBlocks = (0, utils_1.splitSelectorAndProperties)(blocks); return splitBlocks.concat(mediaQueries).filter(utils_1.truthyValues); }; const contains = function (output, expected) { const outputBlocks = createSelectorsRulesPairs(output); const expectedBlocks = createSelectorsRulesPairs(expected); const results = expectedBlocks.map((block) => { const matchingOutputBlocks = outputBlocks.filter((element) => element.selector === block.selector); if (matchingOutputBlocks.length) { // Turns a css string into an array of property-value pairs. const expectedProperties = block.output .split(';') .map((propertyValuePair) => propertyValuePair.trim()) .filter((innerBlock) => innerBlock && innerBlock !== ' {' && innerBlock !== '}'); // This is the assertion itself! return expectedProperties.every((property) => matchingOutputBlocks.some((outputBlock) => outputBlock.output.includes(property))); } return false; }); return results.every((result) => result === true); }; const parse = function (rawCss, ctxLines) { const contextLines = typeof ctxLines === 'undefined' ? 10 : ctxLines; const lines = rawCss.split(/\r?\n/); const parseCss = function () { const ctx = { modules: [] }; let handler = parseModule; (0, postcss_1.parse)(rawCss).each((node) => { /* v8 ignore else */ if (['comment', 'rule', 'atrule'].includes(node.type)) { handler = handler(node, ctx); } }); finishCurrentModule(ctx); return ctx.modules; }; const parseError = function (msg, seeking, start) { var _a, _b; const unknown = '<unknown>'; let errorMsg = `Line ${(_a = start === null || start === void 0 ? void 0 : start.line) !== null && _a !== void 0 ? _a : unknown}, ` + `column ${(_b = start === null || start === void 0 ? void 0 : start.column) !== null && _b !== void 0 ? _b : unknown}: ${msg}; ` + `looking for ${seeking || unknown}.`; /* v8 ignore else */ if ((start === null || start === void 0 ? void 0 : start.line) && (start === null || start === void 0 ? void 0 : start.column)) { errorMsg = `${errorMsg}\n` + `-- Context --\n${lines .slice(Math.max(0, start.line - contextLines), start.line) .join('\n')}\n${' '.repeat(start.column - 1)}^\n`; } return new Error(errorMsg); }; const parseModule = function (rule, ctx) { if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); /* v8 ignore else */ if (!text) { return parseModule; } if (text.startsWith(constants.MODULE_TOKEN)) { finishCurrentModule(ctx); ctx.currentModule = { module: text.substring(constants.MODULE_TOKEN.length), tests: [], }; return parseTest; } if (text.startsWith(constants.SUMMARY_TOKEN)) { return ignoreUntilEndSummary; } // ignore un-recognized comments, keep looking for module header. return parseModule; } // ignore other rule types return parseModule; }; // eslint-disable-next-line @typescript-eslint/no-unused-vars const ignoreUntilEndSummary = function (rule, ctx) { var _a; if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); if (text.startsWith(constants.END_SUMMARY_TOKEN)) { return parseModule; } return ignoreUntilEndSummary; } throw parseError(`Unexpected rule type "${rule.type}"`, 'end summary', (_a = rule.source) === null || _a === void 0 ? void 0 : _a.start); }; const parseTest = function (rule, ctx) { if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); /* v8 ignore else */ if (!text) { return parseTest; } if (text.match(/^-+$/)) { return parseTest; } if (text.startsWith(constants.TEST_TOKEN)) { finishCurrentTest(ctx); ctx.currentTest = { test: text.substring(constants.TEST_TOKEN.length), assertions: [], }; return parseAssertion; } return parseModule(rule, ctx); } // ignore other rule types return parseModule; }; const parseAssertion = function (rule, ctx) { if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); /* v8 ignore else */ if (!text) { return parseAssertion; } if (text.startsWith(constants.PASS_TOKEN)) { finishCurrentAssertion(ctx); ctx.currentAssertion = { description: text.substring(constants.PASS_TOKEN.length).trim() || '<no description>', passed: true, }; return parseAssertion; } else if (text.startsWith(constants.FAIL_TOKEN)) { finishCurrentAssertion(ctx); const endAssertionType = text.indexOf(constants.END_FAIL_TOKEN); ctx.currentAssertion = { description: text.substring(endAssertionType + 2).trim(), passed: false, assertionType: text .substring(constants.FAIL_TOKEN.length, endAssertionType) .trim(), }; return parseFailureDetail; } else if (text.startsWith(constants.ASSERT_TOKEN)) { finishCurrentAssertion(ctx); ctx.currentAssertion = { description: text.substring(constants.ASSERT_TOKEN.length).trim(), assertionType: 'equal', }; return parseAssertionOutputStart; } return parseTest(rule, ctx); } // ignore other rule types return parseModule; }; const parseFailureDetail = function (rule, ctx) { var _a; if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); if (text.startsWith(constants.FAILURE_DETAIL_TOKEN)) { const detail = text.substring(constants.FAILURE_DETAIL_TOKEN.length); const isOutput = detail.startsWith(constants.OUTPUT_TOKEN); const isExpected = detail.startsWith(constants.EXPECTED_TOKEN); let outputOrExpected; if (isOutput) { outputOrExpected = 'output'; } else if (isExpected) { outputOrExpected = 'expected'; } /* v8 ignore else */ if (outputOrExpected) { /* v8 ignore else */ if (ctx.currentAssertion) { const startType = text.indexOf(constants.FAILURE_TYPE_START_TOKEN); const endType = text.indexOf(constants.FAILURE_TYPE_END_TOKEN); const type = text.substring(startType, endType + 1); const content = text.substring(endType + 2); ctx.currentAssertion[outputOrExpected] = `${type} ${content}`; } return parseFailureDetail; } const splitAt = detail.indexOf(constants.DETAILS_SEPARATOR_TOKEN); /* v8 ignore else */ if (splitAt !== -1) { /* v8 ignore else */ if (ctx.currentAssertion) { const key = detail.substring(0, splitAt); ctx.currentAssertion[key.toLowerCase()] = detail.substring(splitAt + constants.DETAILS_SEPARATOR_TOKEN.length); } return parseFailureDetail; } } return parseAssertion(rule, ctx); } throw parseError(`Unexpected rule type "${rule.type}"`, 'output/expected', (_a = rule.source) === null || _a === void 0 ? void 0 : _a.start); }; const parseAssertionOutputStart = function (rule, ctx) { var _a, _b; if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); /* v8 ignore else */ if (!text) { return parseAssertionOutputStart; } if (text === constants.OUTPUT_START_TOKEN) { ctx.currentOutputRules = []; return parseAssertionOutput; } throw parseError(`Unexpected comment "${text}"`, 'OUTPUT', (_a = rule.source) === null || _a === void 0 ? void 0 : _a.start); } throw parseError(`Unexpected rule type "${rule.type}"`, 'OUTPUT', (_b = rule.source) === null || _b === void 0 ? void 0 : _b.start); }; const parseAssertionOutput = function (rule, ctx) { var _a; if ((0, utils_1.isCommentNode)(rule)) { if (rule.text.trim() === constants.OUTPUT_END_TOKEN) { /* v8 ignore else */ if (ctx.currentAssertion) { ctx.currentAssertion.output = (0, utils_1.generateCss)(ctx.currentOutputRules || []); } delete ctx.currentOutputRules; return parseAssertionExpectedStart; } } (_a = ctx.currentOutputRules) === null || _a === void 0 ? void 0 : _a.push(rule); return parseAssertionOutput; }; const parseAssertionExpectedStart = function (rule, ctx) { var _a, _b; /* v8 ignore else */ if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); /* v8 ignore else */ if (!text) { return parseAssertionExpectedStart; } if (text === constants.EXPECTED_START_TOKEN) { ctx.currentExpectedRules = []; return parseAssertionExpected; } if (text === constants.CONTAINED_START_TOKEN) { ctx.currentExpectedRules = []; // Initialize array for multiple contains assertions /* v8 ignore else */ if (!ctx.currentExpectedContained) { ctx.currentExpectedContained = []; } return parseAssertionContained; } if (text === constants.CONTAINS_STRING_START_TOKEN) { ctx.currentExpectedRules = []; // Initialize array for multiple contains-string assertions /* v8 ignore else */ if (!ctx.currentExpectedStrings) { ctx.currentExpectedStrings = []; } return parseAssertionContainsString; } throw parseError(`Unexpected comment "${text}"`, 'EXPECTED', (_a = rule.source) === null || _a === void 0 ? void 0 : _a.start); } throw parseError(`Unexpected rule type "${rule.type}"`, 'EXPECTED', (_b = rule.source) === null || _b === void 0 ? void 0 : _b.start); }; const parseAssertionExpected = function (rule, ctx) { var _a; /* v8 ignore else */ if ((0, utils_1.isCommentNode)(rule)) { if (rule.text.trim() === constants.EXPECTED_END_TOKEN) { /* v8 ignore else */ if (ctx.currentAssertion) { ctx.currentAssertion.expected = (0, utils_1.generateCss)(ctx.currentExpectedRules || []); ctx.currentAssertion.passed = ctx.currentAssertion.output === ctx.currentAssertion.expected; } delete ctx.currentExpectedRules; return parseEndAssertion; } } (_a = ctx.currentExpectedRules) === null || _a === void 0 ? void 0 : _a.push(rule); return parseAssertionExpected; }; const parseEndAssertion = function (rule, ctx) { var _a, _b; /* v8 ignore else */ if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); /* v8 ignore else */ if (!text) { return parseEndAssertion; } /* v8 ignore else */ if (text === constants.ASSERT_END_TOKEN) { finishCurrentAssertion(ctx); return parseAssertion; } throw parseError(`Unexpected comment "${text}"`, 'END_ASSERT', (_a = rule.source) === null || _a === void 0 ? void 0 : _a.start); } throw parseError(`Unexpected rule type "${rule.type}"`, 'END_ASSERT', (_b = rule.source) === null || _b === void 0 ? void 0 : _b.start); }; const parseAssertionContained = function (rule, ctx) { var _a, _b; if ((0, utils_1.isCommentNode)(rule) && rule.text.trim() === constants.CONTAINED_END_TOKEN) { const expectedCss = (0, utils_1.generateCss)(ctx.currentExpectedRules || []); // Add this expected CSS block to the array (_a = ctx.currentExpectedContained) === null || _a === void 0 ? void 0 : _a.push(expectedCss); delete ctx.currentExpectedRules; return parseAssertionContainedEnd; } (_b = ctx.currentExpectedRules) === null || _b === void 0 ? void 0 : _b.push(rule); return parseAssertionContained; }; const parseAssertionContainedEnd = function (rule, ctx) { var _a, _b; /* v8 ignore else */ if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); /* v8 ignore else */ if (!text) { return parseAssertionContainedEnd; } // Check for another CONTAINED block /* v8 ignore else */ if (text === constants.CONTAINED_START_TOKEN) { ctx.currentExpectedRules = []; return parseAssertionContained; } // Check for END_ASSERT - finalize the assertion /* v8 ignore else */ if (text === constants.ASSERT_END_TOKEN) { /* v8 ignore else */ if (ctx.currentAssertion && ctx.currentExpectedContained) { // Check if all expected CSS blocks are found in the output const allFound = ctx.currentExpectedContained.every((expectedCss) => { var _a; return contains(((_a = ctx.currentAssertion) === null || _a === void 0 ? void 0 : _a.output) || '', expectedCss); }); ctx.currentAssertion.passed = allFound; ctx.currentAssertion.assertionType = 'contains'; // Store all expected CSS blocks joined with newlines for display ctx.currentAssertion.expected = ctx.currentExpectedContained.join('\n---\n'); } delete ctx.currentExpectedContained; finishCurrentAssertion(ctx); return parseAssertion; } throw parseError(`Unexpected comment "${text}"`, 'CONTAINED or END_ASSERT', (_a = rule.source) === null || _a === void 0 ? void 0 : _a.start); } throw parseError(`Unexpected rule type "${rule.type}"`, 'CONTAINED or END_ASSERT', (_b = rule.source) === null || _b === void 0 ? void 0 : _b.start); }; const parseAssertionContainsString = function (rule, ctx) { var _a, _b; if ((0, utils_1.isCommentNode)(rule) && rule.text.trim() === constants.CONTAINS_STRING_END_TOKEN) { // The string to find is wrapped in a Sass comment because it might not // always be a complete, valid CSS block on its own. These replace calls // are necessary to strip the leading `/*` and trailing `*/` characters // that enclose the string, so we're left with just the raw string to // find for accurate comparison. const expectedString = (0, utils_1.generateCss)(ctx.currentExpectedRules || []) .replace(new RegExp('^/\\*'), '') .replace(new RegExp('\\*/$'), '') .trim(); // Add this string to the array of expected strings (_a = ctx.currentExpectedStrings) === null || _a === void 0 ? void 0 : _a.push(expectedString); delete ctx.currentExpectedRules; return parseAssertionContainsStringEnd; } (_b = ctx.currentExpectedRules) === null || _b === void 0 ? void 0 : _b.push(rule); return parseAssertionContainsString; }; const parseAssertionContainsStringEnd = function (rule, ctx) { var _a, _b; /* v8 ignore else */ if ((0, utils_1.isCommentNode)(rule)) { const text = rule.text.trim(); /* v8 ignore else */ if (!text) { return parseAssertionContainsStringEnd; } // Check for another CONTAINS_STRING block /* v8 ignore else */ if (text === constants.CONTAINS_STRING_START_TOKEN) { ctx.currentExpectedRules = []; return parseAssertionContainsString; } // Check for END_ASSERT - finalize the assertion /* v8 ignore else */ if (text === constants.ASSERT_END_TOKEN) { /* v8 ignore else */ if (ctx.currentAssertion && ctx.currentExpectedStrings) { // Check if all expected strings are found in the output const allFound = ctx.currentExpectedStrings.every((str) => { var _a, _b; return (_b = (_a = ctx.currentAssertion) === null || _a === void 0 ? void 0 : _a.output) === null || _b === void 0 ? void 0 : _b.includes(str); }); ctx.currentAssertion.passed = allFound; ctx.currentAssertion.assertionType = 'contains-string'; // Store all expected strings joined with newlines for display ctx.currentAssertion.expected = ctx.currentExpectedStrings.join('\n'); } delete ctx.currentExpectedStrings; finishCurrentAssertion(ctx); return parseAssertion; } throw parseError(`Unexpected comment "${text}"`, 'CONTAINS_STRING or END_ASSERT', (_a = rule.source) === null || _a === void 0 ? void 0 : _a.start); } throw parseError(`Unexpected rule type "${rule.type}"`, 'CONTAINS_STRING or END_ASSERT', (_b = rule.source) === null || _b === void 0 ? void 0 : _b.start); }; return parseCss(); }; exports.parse = parse; //# sourceMappingURL=index.js.map