'use strict'; var fs = require('fs'); var path = require('path'); var root = path.resolve(__dirname, '..'); var passed = 0; function read(file) { return fs.readFileSync(path.join(root, file), 'utf8'); } function json(file) { return JSON.parse(read(file)); } function assert(condition, message) { if (!condition) { throw new Error(message); } passed++; } var packageJson = json('package.json'); var solution = json('config/package-solution.json').solution; var config = json('config/config.json'); var manifest = json('src/webparts/accordion/AccordionWebPart.manifest.json'); var webPart = read('src/webparts/accordion/AccordionWebPart.ts'); var component = read('src/webparts/accordion/components/Accordion.tsx'); var sanitizer = read('src/webparts/accordion/AccordionHtmlSanitizer.ts'); var styles = read('src/webparts/accordion/components/Accordion.module.scss'); assert(packageJson.version === '1.0.0', 'Package version must be 1.0.0.'); assert(solution.version === '1.0.0.0', 'Solution version must be 1.0.0.0.'); assert(solution.includeClientSideAssets === true, 'Client-side assets must be included.'); assert(solution.skipFeatureDeployment === true, 'Solution must support central deployment.'); assert(manifest.componentType === 'WebPart', 'Component must be a web part.'); assert(!!config.bundles['accordion-web-part'], 'Accordion bundle is missing.'); assert(webPart.indexOf("PropertyPaneDropdown('listId'") >= 0, 'Dynamic list selection is missing.'); assert(webPart.indexOf("PropertyPaneDropdown('contentField'") >= 0, 'Dynamic content field selection is missing.'); assert(webPart.indexOf("PropertyPaneDropdown('categoryField'") >= 0, 'Category field selection is missing.'); assert(component.indexOf("aria-expanded={open}") >= 0 && component.indexOf("role='region'") >= 0, 'Accessible accordion semantics are missing.'); assert(component.indexOf('sanitizeAccordionHtml(item.content)') >= 0, 'Rich text is not sanitized before rendering.'); var allowedTags = sanitizer.substring(sanitizer.indexOf('const AllowedTags'), sanitizer.indexOf('const DropContentTags')); assert(allowedTags.indexOf("'script'") < 0 && allowedTags.indexOf("'img'") < 0 && sanitizer.indexOf("'script'") >= 0, 'Unsafe rich-text elements must be removed rather than allow-listed.'); ['neutralPrimary', 'neutralLight', 'neutralLighter', 'themePrimary', 'white'].forEach(function (slot) { assert(styles.indexOf('[theme: ' + slot + ', default:') >= 0, 'SharePoint theme slot is missing: ' + slot); }); console.log('Accordion project validation passed:', passed);