|
1 | 1 | 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"; |
3 | 4 |
|
4 | 5 | export class ElementAssertion<T extends Element> extends Assertion<T> { |
5 | 6 |
|
@@ -147,112 +148,50 @@ export class ElementAssertion<T extends Element> extends Assertion<T> { |
147 | 148 | return this.actual.className.split(/\s+/).filter(Boolean); |
148 | 149 | } |
149 | 150 |
|
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 | + */ |
186 | 157 |
|
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; |
190 | 164 |
|
191 | | - }; |
| 165 | + const window = this.actual.ownerDocument.defaultView; |
| 166 | + const computedStyle = window?.getComputedStyle; |
192 | 167 |
|
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; |
205 | 170 |
|
206 | | - expectedStyle = { |
207 | | - ...expectedStyle, |
208 | | - [property]: normalizedValue.trim(), |
209 | | - }; |
210 | 171 |
|
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!); |
228 | 176 |
|
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); |
251 | 178 |
|
| 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 | + }); |
252 | 191 | return this.execute({ |
253 | | - assertWhen: isSameStyle, |
| 192 | + assertWhen: isSameStyle(expectedStyle, receivedStyle), |
254 | 193 | error, |
255 | | - invertedError |
| 194 | + invertedError, |
256 | 195 | }); |
257 | 196 | } |
258 | 197 | return this; |
|
0 commit comments