sass-true
Version:
Unit testing for Sass.
665 lines • 30.3 kB
JavaScript
;
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