Skip to content

Commit b0573a5

Browse files
committed
Add: functions moved into helpers and implemeneted in class
1 parent 5ba57a6 commit b0573a5

File tree

2 files changed

+137
-98
lines changed

2 files changed

+137
-98
lines changed

packages/dom/src/lib/ElementAssertion.ts

Lines changed: 37 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Assertion, AssertionError } from "@assertive-ts/core";
2-
import {parse} from '@adobe/css-tools'
2+
import { parse } from "@adobe/css-tools";
3+
import { CssAtRuleAST, getProps, isSameStyle, normalizeStylesObject, normalizeStylesString } from "./helpers/helpers";
34

45
export class ElementAssertion<T extends Element> extends Assertion<T> {
56

@@ -147,112 +148,50 @@ export class ElementAssertion<T extends Element> extends Assertion<T> {
147148
return this.actual.className.split(/\s+/).filter(Boolean);
148149
}
149150

150-
public toHaveStyle(css: Object|string): this {
151-
if (
152-
this.actual instanceof HTMLElement ||
153-
this.actual['ownerDocument']
154-
) {
155-
156-
const parsedCSS = typeof css === 'object'
157-
? css
158-
: parse(`selector { ${css} }`, {silent: true}).stylesheet
159-
160-
const window = this.actual.ownerDocument.defaultView;
161-
162-
const computedStyle = window?.getComputedStyle;
163-
164-
const expected = parsedCSS
165-
console.log("expected: ", expected);
166-
const received = computedStyle?.(this.actual);
167-
168-
interface StyleDeclaration {
169-
property: string;
170-
value: string;
171-
}
172-
173-
let expectedStyle = {}
174-
let receivedStyle = {}
175-
let props: string[] = []
176-
177-
const normalizer = document.createElement("div");
178-
document.body.appendChild(normalizer);
179-
180-
if (typeof css === 'object') {
181-
Object.entries(css).map(([property, value]) => {
182-
props = [...props, property];
183-
184-
normalizer.style[property] = value;
185-
const normalizedValue = window?.getComputedStyle(normalizer).getPropertyValue(property);
151+
/**
152+
* Asserts that the element has the specified CSS styles.
153+
*
154+
* @param css - The expected CSS styles.
155+
* @returns The assertion instance.
156+
*/
186157

187-
expectedStyle = {
188-
...expectedStyle,
189-
[property]: normalizedValue?.trim(),
158+
public toHaveStyle(css: Object | string): this {
159+
if (this.actual instanceof HTMLElement || this.actual["ownerDocument"]) {
160+
const parsedCSS =
161+
typeof css === "object"
162+
? css
163+
: parse(`selector { ${css} }`, { silent: true }).stylesheet;
190164

191-
};
165+
const window = this.actual.ownerDocument.defaultView;
166+
const computedStyle = window?.getComputedStyle;
192167

193-
});
194-
console.log("EXPECTED STYLE: ", expectedStyle);
195-
} else {
196-
const expectedRule = expected.rules[0];
197-
expectedRule.declarations.map((declaration: StyleDeclaration) => {
198-
const property = declaration.property;
199-
const value = declaration.value;
200-
201-
props = [...props, property];
202-
203-
normalizer.style[property] = value;
204-
const normalizedValue = window.getComputedStyle(normalizer).getPropertyValue(property);
168+
const expected = parsedCSS as CssAtRuleAST;
169+
const received = computedStyle?.(this.actual) as CSSStyleDeclaration;
205170

206-
expectedStyle = {
207-
...expectedStyle,
208-
[property]: normalizedValue.trim(),
209-
};
210171

211-
return expectedStyle;
212-
});
213-
}
214-
215-
document.body.removeChild(normalizer);
216-
217-
218-
console.log("expected style: ",expectedStyle);
219-
220-
props.map((prop: string) => {
221-
receivedStyle = {
222-
...receivedStyle,
223-
[prop]: received?.getPropertyValue(prop).trim(),
224-
};
225-
})
226-
227-
console.log("received style: ", receivedStyle);
172+
const { props, expectedStyle } =
173+
typeof css === "object"
174+
? normalizeStylesObject(css, window!)
175+
: normalizeStylesString(expected, window!);
228176

229-
const isSameStyle = !!Object.keys(expectedStyle).length &&
230-
Object.entries(expectedStyle).every(([expectedProp, expectedValue]) => {
231-
const isCustomProperty = expectedProp.startsWith('--')
232-
const spellingVariants = [expectedProp]
233-
expectedProp !== null;
234-
235-
if (!isCustomProperty) spellingVariants.push(expectedProp.toLowerCase())
236-
return spellingVariants.some( searchProp =>
237-
receivedStyle[searchProp] === expectedValue
238-
)
239-
})
240-
241-
console.log("isSameStyle: ", isSameStyle)
242-
const error = new AssertionError({
243-
actual: this.actual,
244-
message: `Expected the element to have ${JSON.stringify(expectedStyle)} style`,
245-
expected: expectedStyle
246-
})
247-
const invertedError = new AssertionError({
248-
actual: this.actual,
249-
message: `Expected the element to NOT have ${JSON.stringify(expectedStyle)} style`,
250-
})
177+
const receivedStyle = getProps(props, received);
251178

179+
const error = new AssertionError({
180+
actual: this.actual,
181+
message: `Expected the element to have ${JSON.stringify(expectedStyle
182+
)} style`,
183+
expected: expectedStyle,
184+
});
185+
const invertedError = new AssertionError({
186+
actual: this.actual,
187+
message: `Expected the element to NOT have ${JSON.stringify(
188+
expectedStyle
189+
)} style`,
190+
});
252191
return this.execute({
253-
assertWhen: isSameStyle,
192+
assertWhen: isSameStyle(expectedStyle, receivedStyle),
254193
error,
255-
invertedError
194+
invertedError,
256195
});
257196
}
258197
return this;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
export interface CssAtRuleAST {
2+
rules: Rule[];
3+
declarations: StyleDeclaration[];
4+
}
5+
6+
interface Rule {
7+
selectors: string[];
8+
declarations: StyleDeclaration[];
9+
}
10+
11+
interface StyleDeclaration extends Record<string, string> {
12+
property: string;
13+
value: string;
14+
}
15+
16+
export const normalizeStylesObject = (
17+
css: Object,
18+
window: Window
19+
): { props: string[]; expectedStyle: StyleDeclaration } => {
20+
const normalizer = document.createElement("div");
21+
document.body.appendChild(normalizer);
22+
23+
const { props, expectedStyle } = Object.entries(css).reduce(
24+
(acc, [property, value]) => {
25+
normalizer.style.setProperty(property, value);
26+
27+
const normalizedValue = window
28+
.getComputedStyle(normalizer)
29+
.getPropertyValue(property)
30+
.trim();
31+
32+
return {
33+
props: [...acc.props, property],
34+
expectedStyle: {
35+
...acc.expectedStyle,
36+
[property]: normalizedValue,
37+
},
38+
};
39+
},
40+
{ props: [] as string[], expectedStyle: {} as StyleDeclaration }
41+
);
42+
43+
document.body.removeChild(normalizer);
44+
45+
return { props, expectedStyle };
46+
};
47+
48+
export const normalizeStylesString = (expectedRule: CssAtRuleAST, window: Window) => {
49+
const normalizer = document.createElement("div");
50+
document.body.appendChild(normalizer);
51+
52+
const rules = expectedRule?.rules[0] || { declarations: [] };
53+
const { props, expectedStyle } = rules?.declarations.reduce(
54+
(acc, { property, value }) => {
55+
normalizer.style.setProperty(property, value);
56+
57+
const normalizedValue = window
58+
.getComputedStyle(normalizer)
59+
.getPropertyValue(property)
60+
.trim();
61+
62+
return {
63+
props: [...acc.props, property],
64+
expectedStyle: {
65+
...acc.expectedStyle,
66+
[property]: normalizedValue,
67+
},
68+
};
69+
},
70+
{ props: [] as string[], expectedStyle: {} as StyleDeclaration }
71+
);
72+
73+
document.body.removeChild(normalizer);
74+
75+
return { props, expectedStyle };
76+
};
77+
78+
export const getProps = (props : string[], received: CSSStyleDeclaration) => {
79+
return props.reduce((acc, prop) => {
80+
acc[prop] = received?.getPropertyValue(prop).trim();
81+
return acc;
82+
}, {} as StyleDeclaration);
83+
84+
};
85+
86+
export const isSameStyle = (expectedStyle: StyleDeclaration, receivedStyle: StyleDeclaration): boolean => {
87+
return !!Object.keys(expectedStyle).length &&
88+
Object.entries(expectedStyle).every(([expectedProp, expectedValue]) => {
89+
const isCustomProperty = expectedProp.startsWith("--");
90+
const spellingVariants = [expectedProp];
91+
expectedProp !== null;
92+
93+
if (!isCustomProperty)
94+
spellingVariants.push(expectedProp.toLowerCase());
95+
return spellingVariants.some(
96+
(searchProp) => receivedStyle[searchProp] === expectedValue
97+
);
98+
});
99+
}
100+

0 commit comments

Comments
 (0)