Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/dom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"test": "NODE_ENV=test mocha"
},
"dependencies": {
"@adobe/css-tools": "^4.4.3",
"fast-deep-equal": "^3.1.3",
"tslib": "^2.6.2"
},
Expand Down
113 changes: 113 additions & 0 deletions packages/dom/src/lib/ElementAssertion.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Assertion, AssertionError } from "@assertive-ts/core";
import {parse} from '@adobe/css-tools'

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

Expand Down Expand Up @@ -146,6 +147,118 @@ export class ElementAssertion<T extends Element> extends Assertion<T> {
return this.actual.className.split(/\s+/).filter(Boolean);
}

public toHaveStyle(css: Object|string): this {
if (
this.actual instanceof HTMLElement ||
this.actual['ownerDocument']
) {

const parsedCSS = typeof css === 'object'
? css
: parse(`selector { ${css} }`, {silent: true}).stylesheet

const window = this.actual.ownerDocument.defaultView;

const computedStyle = window?.getComputedStyle;

const expected = parsedCSS
console.log("expected: ", expected);
const received = computedStyle?.(this.actual);

interface StyleDeclaration {
property: string;
value: string;
}

let expectedStyle = {}
let receivedStyle = {}
let props: string[] = []

const normalizer = document.createElement("div");
document.body.appendChild(normalizer);

if (typeof css === 'object') {
Object.entries(css).map(([property, value]) => {
props = [...props, property];

normalizer.style[property] = value;
const normalizedValue = window?.getComputedStyle(normalizer).getPropertyValue(property);

expectedStyle = {
...expectedStyle,
[property]: normalizedValue?.trim(),

};

});
console.log("EXPECTED STYLE: ", expectedStyle);
} else {
const expectedRule = expected.rules[0];
expectedRule.declarations.map((declaration: StyleDeclaration) => {
const property = declaration.property;
const value = declaration.value;

props = [...props, property];

normalizer.style[property] = value;
const normalizedValue = window.getComputedStyle(normalizer).getPropertyValue(property);

expectedStyle = {
...expectedStyle,
[property]: normalizedValue.trim(),
};

return expectedStyle;
});
}

document.body.removeChild(normalizer);


console.log("expected style: ",expectedStyle);

props.map((prop: string) => {
receivedStyle = {
...receivedStyle,
[prop]: received?.getPropertyValue(prop).trim(),
};
})

console.log("received style: ", receivedStyle);

const isSameStyle = !!Object.keys(expectedStyle).length &&
Object.entries(expectedStyle).every(([expectedProp, expectedValue]) => {
const isCustomProperty = expectedProp.startsWith('--')
const spellingVariants = [expectedProp]
expectedProp !== null;

if (!isCustomProperty) spellingVariants.push(expectedProp.toLowerCase())
return spellingVariants.some( searchProp =>
receivedStyle[searchProp] === expectedValue
)
})

console.log("isSameStyle: ", isSameStyle)
const error = new AssertionError({
actual: this.actual,
message: `Expected the element to have ${JSON.stringify(expectedStyle)} style`,
expected: expectedStyle
})
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected the element to NOT have ${JSON.stringify(expectedStyle)} style`,
})

return this.execute({
assertWhen: isSameStyle,
error,
invertedError
});
}
return this;
}


/**
* Helper method to assert the presence or absence of class names.
*
Expand Down
42 changes: 40 additions & 2 deletions packages/dom/test/unit/lib/ElementAssertion.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AssertionError, expect } from "@assertive-ts/core";
import { render } from "@testing-library/react";
import { getByTestId, render } from "@testing-library/react";

import { ElementAssertion } from "../../../src/lib/ElementAssertion";

Expand Down Expand Up @@ -265,4 +265,42 @@ describe("[Unit] ElementAssertion.test.ts", () => {
});
});

});
describe(".toHaveStyle", () => {
context("when the element has the expected style when passed as string", () => {
it("returns the assertion instance when the styles are the same", () => {
const { getByTestId } = render(<div className="foo bar test" style={{ display: "flex", color: "red", border: "1px solid black" }} data-testid="test-div" />);
const divTest = getByTestId("test-div");
const test = new ElementAssertion(divTest);

expect(test.toHaveStyle("display: flex; color: red; border: 1px solid black")).toBeEqual(test);

});
it("fails the assertion when the styles are not the same", () => {
const { getByTestId } = render(<div className="foo bar test" style={{ display: "flex", color: "red" }} data-testid="test-div" />);
const divTest = getByTestId("test-div");
const test = new ElementAssertion(divTest);

expect(test.toHaveStyle("color: red; display: flex; border: 1px solid black;")).toBeEqual(test);

});
context("when the element has the expected style when passed as object", () => {
it("returns the assertion instance when the styles are the same", () => {
const { getByTestId } = render(<div className="foo bar test" style={{ display: "flex", color: "red", border: "1px solid black" }} data-testid="test-div" />);
const divTest = getByTestId("test-div");
const test = new ElementAssertion(divTest);

expect(test.toHaveStyle({color: "red", display: "flex", border: "1px solid black"})).toBeEqual(test);

});
it("fails the assertion when the styles are not the same", () => {
const { getByTestId } = render(<div className="foo bar test" style={{ display: "flex", color: "red" }} data-testid="test-div" />);
const divTest = getByTestId("test-div");
const test = new ElementAssertion(divTest);

expect(test.toHaveStyle({color: "red", display: "flex", border: "1px solid black"})).toBeEqual(test);

});
});
})
})
})
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ __metadata:
languageName: node
linkType: hard

"@adobe/css-tools@npm:^4.4.3":
version: 4.4.3
resolution: "@adobe/css-tools@npm:4.4.3"
checksum: 10/701379c514b7a43ca6681705a93cd57ad79565cfef9591122e9499897550cf324a5e5bb1bc51df0e7433cf0e91b962c90f18ac459dcc98b2431daa04aa63cb20
languageName: node
linkType: hard

"@ampproject/remapping@npm:^2.2.0":
version: 2.2.1
resolution: "@ampproject/remapping@npm:2.2.1"
Expand Down Expand Up @@ -49,6 +56,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@assertive-ts/dom@workspace:packages/dom"
dependencies:
"@adobe/css-tools": "npm:^4.4.3"
"@assertive-ts/core": "workspace:^"
"@testing-library/dom": "npm:^10.1.0"
"@testing-library/react": "npm:^16.0.0"
Expand Down
Loading