I also ran into some issues with getComputedStyle with jsdom in Typescript. I was trying to be able to test rendered CSS better. I decided to instead write a small helper function to help me easily get CSS styles from DebugElement / DebugElement[] / string / string[]. I hope this is useful to someone!
My Helper Function:
//IMPORTS REMOVED FOR BREVITY
/**
* Checks if an unknown object is a string using typeof
* @param obj an object of unknown type that is being type-checked
* @returns true if the object is a string, false otherwise
*/
function isString(obj: unknown): boolean {
return typeof(obj) === 'string';
}
/**
* Helper function to help get a list of all rendered CSS styles from a CSS query or Debug Element.
*
* Overloads:
* getStyleByCSS(debugElement: DebugElement)
* Gets the styles from a debug element and returns the style declarations.
*
* getStyleByCSS(debugElement: DebugElement[])
* Gets the styles for each debug element in an array and returns an array of objects containing each debug element and it's respective styles.
*
* getStyleByCSS(debugElement: string | string[], fixture: ComponentFixture<unknown>)
* Queries a given fixture for ALL debug elements for a given CSS query/queries and returns an array of objects containing each debug element
* and it's respective styles.
*
*/
export function getStyleByCSS(queryEl: DebugElement): CSSStyleDeclaration;
export function getStyleByCSS(queryEl: DebugElement[]): ElementWithStyles[];
export function getStyleByCSS(queryEl: string | string[], fixture: ComponentFixture<unknown>): ElementWithStyles[];
export function getStyleByCSS(queryEl: unknown, fixture?: ComponentFixture<unknown>): CSSStyleDeclaration | ElementWithStyles[] {
// If we are using the Overload that just takes in a DebugElement
if(!isString(queryEl) && !Array.isArray(queryEl)) {
let stylesToReturn: CSSStyleDeclaration = ((queryEl as DebugElement).styles._values) as unknown as CSSStyleDeclaration;
return stylesToReturn as CSSStyleDeclaration;
// If we are using the Overload that just takes in a string and a fixture object
} else if (isString(queryEl) && !Array.isArray(queryEl)) {
let returnElements: ElementWithStyles[] = [];
let debugElements = fixture.debugElement.queryAll(By.css(queryEl as string));
// Check each debug element queried by the given string and format an array of objects containing each debug element and it's respective styles.
debugElements.forEach(el => {
let thisElWithStyles: ElementWithStyles = {
debugElement: el,
styles: ((el as DebugElement).styles._values) as unknown as CSSStyleDeclaration
};
returnElements.push(thisElWithStyles);
});
return returnElements as ElementWithStyles[];
} else if(Array.isArray(queryEl)) {
// Can't get the styles of an empty array of queries / debug elements, so this situation is invalid.
if(queryEl.length === 0) {
throw new Error('Cannot get CSS declarations of an array of length 0');
} else {
let returnElements: ElementWithStyles[] = [];
// If we are using the Overload that just takes in a DebugElement[]
if(!isString(queryEl[0])) {
// Check each debug element and format an array of objects containing each debug element and it's respective styles.
queryEl.forEach(el => {
let thisElWithStyles: ElementWithStyles = {
debugElement: el,
styles: ((el as DebugElement).styles._values) as unknown as CSSStyleDeclaration
};
returnElements.push(thisElWithStyles);
});
return returnElements as ElementWithStyles[];
// If we are using the Overload that just takes in a string[]
} else if(isString(queryEl[0])) {
let returnEls: ElementWithStyles[] = [];
let debugElements: DebugElement[] = [];
// Get all debug elements for each string in the array of CSS queries
queryEl.forEach(el => {
let thisEl: DebugElement[] = fixture.debugElement.queryAll(By.css(el as string));
thisEl.forEach(y => {
debugElements.push(y);
});
});
// Check each debug element queried by the given strings and format an array of objects containing each debug element and it's respective styles.
debugElements.forEach(el => {
let thisElWithStyles: ElementWithStyles = {
debugElement: el,
styles: ((el as DebugElement).styles._values) as unknown as CSSStyleDeclaration
};
returnEls.push(thisElWithStyles);
});
return returnEls as ElementWithStyles[];
} else {
// If we reach this point, an invalid type was passed into the array
throw new Error('Invalid array type. Must be of type DebugElement or String');
}
}
} else {
// If we reach this point, an invalid type was passed into the function
throw new Error('Invalid type. Please use a DebugElement or string');
}
}
/**
* Interface used to defined the variables that are being returned by various queries, contains a debug element and it's respective styles.
*/
export interface ElementWithStyles {
debugElement: DebugElement;
styles: CSSStyleDeclaration;
}
My Usage:
// Majority of code removed for Brevity
let queriedDebugElements = fixture.debugElement.queryAll(By.css('.my-query-class'));
// Get the rendered CSS styling for each queried object
let elementStyles: ElementWithStyles[] = getStyleByCSS(shownChunks);
// Various usages
// Find all queried objects with a given background color
let elementByBackground = elementStyles.map(x => x.styles).filter(x => x.background === 'rgb(256, 256, 256)');
// Find all queried objects with a width that matches the width of the first element
let elementWithSameWidthAsFirst = elementStyles.map(x => x.styles).filter(x => x.width === elementStyles[0].styles.width);
console.log ...
withsetTimeout
? – Morven