diff --git a/packages/happy-dom/src/css/CSSStyleSheet.ts b/packages/happy-dom/src/css/CSSStyleSheet.ts index 2396b31ac..5ec9943a1 100644 --- a/packages/happy-dom/src/css/CSSStyleSheet.ts +++ b/packages/happy-dom/src/css/CSSStyleSheet.ts @@ -5,7 +5,7 @@ import CSSRule from './CSSRule'; import MediaList from './MediaList'; /** - * Attr node interface. + * CSS StyleSheet. * * Reference: * https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet. @@ -16,20 +16,16 @@ export default class CSSStyleSheet { public namespaceURI: string = null; public readonly cssRules: CSSRule[] = []; - // Constructable Stylesheets is a new feature that only Blink supports: - // https://wicg.github.io/construct-stylesheets/ - // TODO: Not fully implemented. + // TODO: MediaList is not fully implemented. public media: MediaList | string; public title: string; public alternate: boolean; public disabled: boolean; + private _currentText: string = null; /** * Constructor. * - * Constructable Stylesheets is a new feature that only Blink supports: - * https://wicg.github.io/construct-stylesheets/. - * * @param [options] Options. * @param [options.media] Media. * @param [options.title] Title. @@ -51,9 +47,7 @@ export default class CSSStyleSheet { /** * Inserts a rule. * - * Constructable Stylesheets is a new feature that only Blink supports: - * https://wicg.github.io/construct-stylesheets/. - * + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule * @param rule Rule. * @param [index] Index. * @returns The newly inserterted rule's index. @@ -79,8 +73,7 @@ export default class CSSStyleSheet { DOMExceptionNameEnum.indexSizeError ); } - this.cssRules.splice(index, 0); - this.cssRules.push(rules[0]); + this.cssRules.splice(index, 0, rules[0]); return index; } @@ -94,9 +87,7 @@ export default class CSSStyleSheet { /** * Removes a rule. * - * Constructable Stylesheets is a new feature that only Blink supports: - * https://wicg.github.io/construct-stylesheets/. - * + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/deleteRule * @param index Index. */ public deleteRule(index: number): void { @@ -106,9 +97,7 @@ export default class CSSStyleSheet { /** * Replaces all CSS rules. * - * Constructable Stylesheets is a new feature that only Blink supports: - * https://wicg.github.io/construct-stylesheets/. - * + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/replace * @param text CSS text. * @returns Promise. */ @@ -119,12 +108,13 @@ export default class CSSStyleSheet { /** * Replaces all CSS rules. * - * Constructable Stylesheets is a new feature that only Blink supports: - * https://wicg.github.io/construct-stylesheets/. - * + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/replaceSync * @param text CSS text. */ public replaceSync(text: string): void { - (this.cssRules) = CSSParser.parseFromString(this, text); + if (this._currentText !== text) { + this._currentText = text; + (this.cssRules) = CSSParser.parseFromString(this, text); + } } } diff --git a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyGetParser.ts b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyGetParser.ts index 6f85c7632..1270d51ae 100644 --- a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyGetParser.ts +++ b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyGetParser.ts @@ -255,7 +255,10 @@ export default class CSSStyleDeclarationPropertyGetParser { } /** + * Returns border image. * + * @param properties Properties. + * @returns Property value */ public static getBorderImage(properties: { [k: string]: ICSSStyleDeclarationPropertyValue; diff --git a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyManager.ts b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyManager.ts index 52185bde8..6e7b9e47d 100644 --- a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyManager.ts +++ b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertyManager.ts @@ -4,6 +4,15 @@ import CSSStyleDeclarationValueParser from './CSSStyleDeclarationValueParser'; import CSSStyleDeclarationPropertyGetParser from './CSSStyleDeclarationPropertyGetParser'; import CSSStyleDeclarationCSSParser from './CSSStyleDeclarationCSSParser'; +const TO_STRING_SHORTHAND_PROPERTIES = [ + ['margin'], + ['padding'], + ['border', ['border-width', 'border-style', 'border-color', 'border-image']], + ['border-radius'], + ['background', 'background-position'], + ['font'] +]; + /** * Computed this.properties property parser. */ @@ -465,6 +474,9 @@ export default class CSSStyleDeclarationPropertyManager { case 'text-transform': properties = CSSStyleDeclarationPropertySetParser.getTextTransform(value, important); break; + case 'visibility': + properties = CSSStyleDeclarationPropertySetParser.getVisibility(value, important); + break; default: const trimmedValue = value.trim(); if (trimmedValue) { @@ -526,22 +538,15 @@ export default class CSSStyleDeclarationPropertyManager { const clone = this.clone(); const properties = {}; - for (const fallbackNames of [ - ['margin'], - ['padding'], - ['border', ['border-width', 'border-style', 'border-color', 'border-image']], - ['border-radius'], - ['background', 'background-position'], - ['font'] - ]) { - for (const fallbackName of fallbackNames) { - if (Array.isArray(fallbackName)) { + for (const shorthandPropertyGroup of TO_STRING_SHORTHAND_PROPERTIES) { + for (const shorthandProperty of shorthandPropertyGroup) { + if (Array.isArray(shorthandProperty)) { let isMatch = false; - for (const childFallbackName of fallbackName) { - const property = clone.get(childFallbackName); + for (const childShorthandProperty of shorthandProperty) { + const property = clone.get(childShorthandProperty); if (property) { - properties[childFallbackName] = property; - clone.remove(childFallbackName); + properties[childShorthandProperty] = property; + clone.remove(childShorthandProperty); isMatch = true; } } @@ -549,10 +554,10 @@ export default class CSSStyleDeclarationPropertyManager { break; } } else { - const property = clone.get(fallbackName); + const property = clone.get(shorthandProperty); if (property) { - properties[fallbackName] = property; - clone.remove(fallbackName); + properties[shorthandProperty] = property; + clone.remove(shorthandProperty); break; } } diff --git a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertySetParser.ts b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertySetParser.ts index 44bd9373d..9ba8c931a 100644 --- a/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertySetParser.ts +++ b/packages/happy-dom/src/css/declaration/utilities/CSSStyleDeclarationPropertySetParser.ts @@ -89,6 +89,7 @@ const TEXT_TRANSFORM = [ 'full-width', 'full-size-kana' ]; +const VISIBILITY = ['visible', 'hidden', 'collapse']; /** * Computed style property parser. @@ -2992,4 +2993,34 @@ export default class CSSStyleDeclarationPropertySetParser { } return null; } + + /** + * Returns visibility. + * + * @param value Value. + * @param important Important. + * @returns Property + */ + public static getVisibility( + value: string, + important: boolean + ): { + [key: string]: ICSSStyleDeclarationPropertyValue; + } { + const variable = CSSStyleDeclarationValueParser.getVariable(value); + if (variable) { + return { visibility: { value: variable, important } }; + } + + const lowerValue = value.toLowerCase(); + const parsedValue = + CSSStyleDeclarationValueParser.getGlobal(lowerValue) || + (VISIBILITY.includes(lowerValue) && lowerValue); + if (parsedValue) { + return { + visibility: { value: parsedValue, important } + }; + } + return null; + } } diff --git a/packages/happy-dom/src/file/Blob.ts b/packages/happy-dom/src/file/Blob.ts index bfcfe8842..86cf6d5a3 100644 --- a/packages/happy-dom/src/file/Blob.ts +++ b/packages/happy-dom/src/file/Blob.ts @@ -110,7 +110,6 @@ export default class Blob implements IBlob { return blob; } - /** * Returns a Promise that resolves to a ArrayBuffer. * @@ -120,8 +119,11 @@ export default class Blob implements IBlob { // Reference: // https://github.com/web-std/io/blob/c88170bf24f064adfbb3586a21fb76650ca5a9ab/packages/blob/src/blob.js#L139-L148 // https://stackoverflow.com/questions/8609289/convert-a-binary-nodejs-buffer-to-javascript-arraybuffer + /** + * + */ public async arrayBuffer(): Promise { - return new Uint8Array(this._buffer).buffer + return new Uint8Array(this._buffer).buffer; } /** diff --git a/packages/happy-dom/test/css/CSSStyleSheet.test.ts b/packages/happy-dom/test/css/CSSStyleSheet.test.ts index 5aec1609a..b10f0f181 100644 --- a/packages/happy-dom/test/css/CSSStyleSheet.test.ts +++ b/packages/happy-dom/test/css/CSSStyleSheet.test.ts @@ -14,6 +14,9 @@ describe('CSSStyleSheet', () => { cssStyleSheet.insertRule('div { background-color: green }'); cssStyleSheet.insertRule('span { background-color: green }'); expect(cssStyleSheet.insertRule('button { background-color: green }', 1)).toBe(1); + expect(cssStyleSheet.cssRules[0].cssText).toBe('div { background-color: green; }'); + expect(cssStyleSheet.cssRules[1].cssText).toBe('button { background-color: green; }'); + expect(cssStyleSheet.cssRules[2].cssText).toBe('span { background-color: green; }'); }); it('Inserts a rule.', () => { diff --git a/packages/happy-dom/test/css/declaration/CSSStyleDeclaration.test.ts b/packages/happy-dom/test/css/declaration/CSSStyleDeclaration.test.ts index a76d4054d..749b46402 100644 --- a/packages/happy-dom/test/css/declaration/CSSStyleDeclaration.test.ts +++ b/packages/happy-dom/test/css/declaration/CSSStyleDeclaration.test.ts @@ -1693,7 +1693,7 @@ describe('CSSStyleDeclaration', () => { expect(declaration.font).toBe('small-caps bold 24px / 1 sans-serif'); element.setAttribute('style', 'font: caption'); - debugger; + expect(declaration.font).toBe('caption'); }); }); @@ -1880,6 +1880,27 @@ describe('CSSStyleDeclaration', () => { }); }); + describe('get visibility()', () => { + it('Returns style property.', () => { + const declaration = new CSSStyleDeclaration(element); + + for (const value of [ + 'var(--test-variable)', + 'inherit', + 'initial', + 'revert', + 'unset', + 'visible', + 'hidden', + 'collapse' + ]) { + element.setAttribute('style', `visibility: ${value}`); + + expect(declaration.visibility).toBe(value); + } + }); + }); + describe('get length()', () => { it('Returns length when of styles on element.', () => { const declaration = new CSSStyleDeclaration(element); diff --git a/packages/happy-dom/test/nodes/html-style-element/HTMLStyleElement.test.ts b/packages/happy-dom/test/nodes/html-style-element/HTMLStyleElement.test.ts index 3efcd2a55..6cfcc960c 100644 --- a/packages/happy-dom/test/nodes/html-style-element/HTMLStyleElement.test.ts +++ b/packages/happy-dom/test/nodes/html-style-element/HTMLStyleElement.test.ts @@ -57,11 +57,20 @@ describe('HTMLStyleElement', () => { const textNode = document.createTextNode( 'body { background-color: red }\ndiv { background-color: green }' ); + element.appendChild(textNode); document.head.appendChild(element); + expect(element.sheet.cssRules.length).toBe(2); expect(element.sheet.cssRules[0].cssText).toBe('body { background-color: red; }'); expect(element.sheet.cssRules[1].cssText).toBe('div { background-color: green; }'); + + element.sheet.insertRule('html { background-color: blue }', 0); + + expect(element.sheet.cssRules.length).toBe(3); + expect(element.sheet.cssRules[0].cssText).toBe('html { background-color: blue; }'); + expect(element.sheet.cssRules[1].cssText).toBe('body { background-color: red; }'); + expect(element.sheet.cssRules[2].cssText).toBe('div { background-color: green; }'); }); }); });