我如何断言 HTML 元素在包含样式表后不会改变其外观?
How can I assert that a HTML element does not change its appearance after a stylesheet has been included?
设置
我正在为网站编写插件。它将通过插件的 CSS 向 DOM 添加元素。我希望样式仅限于插件,即一旦插件包含在网页中,插件之外的任何元素都不应更改其外观。
我正在 运行使用 cypress 进行集成测试。当插件包含在页面上时,我如何断言所有预先存在的元素的样式保持不变?我可以访问插件加载前后的页面。
方法
这是我认为应该有效的方法:
cy.visit('theURL');
getStyles().then(oldStyles => { // Get the styles of the elements
mountPlugin(); // Mount the plugin (including CSS)
getStyles().then(newStyles => { // Get the (possibly changed) styles
newStyles.forEach((newStyle, i) => // Compare each element’s style after
expect(newStyle).to.equal(oldStyles[i]) //+ mounting to the state before mounting
);
});
});
function getStyles() {
return cy.get('.el-on-the-page *').then((elements) => { // Get all elements below a certain root
const styles: CSSStyleDeclaration[] = []
elements.each((_, el) => { // Get each element’s style
styles.push(window.getComputedStyle(el)); //+ and put them it an array
});
return styles; // Return the styles
});
}
问题
CSSStyleDeclaration
中的数字键
行 expect(newStyle).to.equal(oldStyles[i])
失败,因为 oldStyles[i]
包含仅列出 属性 名称的数字键。例如,
// oldStyles[i] for some i
{
cssText: "animation-delay: 0s; animation-direction: normal; […more]"
length: 281
parentRule: null
cssFloat: "none"
0: "animation-delay" // <-- These elements only list property names, not values
... //+
280: "line-break" //+
alignContent: "normal" // <-- Only here there are actual property values
... //+
zoom: "1" //+
...
}
解决方法
我通过手动循环 CSS 键并检查键是否为数字来解决此问题。但是,这些数字键只出现在oldStyles
中,而没有出现在newStyles
中。我写这篇文章是因为这对我来说很可疑,我认为错误可能已经存在。
// Instead of newStyles.foreach(…) in the first snippet
newStyles.forEach((newStyle, i) => {
for (const key in newStyle) {
if(isNaN(Number(key))) {
expect(newStyle[key]).to.equal(oldStyles[i][key]);
}
}
});
空 属性 值
我在这里隐含假设 DOM 实际上已加载并应用了样式。根据我的理解,getLinkListStyles
对 cy.get
的调用应该仅在 cy.visit
等待 window 触发 load
之后安排到 运行事件。
cy.visit()
resolves when the remote page fires its load
event.
但是,采用上述解决方法后,我在 oldStyles
中得到了 CSS 规则的空字符串。例如:
//oldStyles[i] for some i
{
cssText: "animation-delay: ; animation-direction: ; animation-duration: ; […more]"
length: 0
parentRule: null
cssFloat: ""
alignContent: ""
...
}
尝试过的解决方案
请注意,当我明确使用带有 cy.visit
的回调时,此行为不会改变,即:
cy.visit(Cypress.env('theURL')).then(()=>{
getStyles().then((oldStyles) => {
// (rest as above)
在getStyles()
开头的cy.wait(15000)
也没有:
function getStyles() {
cy.wait(15000); // The page has definitely loaded and applied all styles by now
cy.get('.el-on-the-page *').then((elements) => {
...
我无法回答有关空 属性 值的问题,解决方法应该不会影响事情。如果我没理解错的话,你在不使用解决方法时会得到 属性 值?
数字键
这些几乎肯定是 cssText 样式的索引,即 内联样式 。
数字键的数量与 cssText
中的条目完全相同,并且值与 cssText
中键值对的 LHS 匹配。
第 2 个 getStyles() 缺少数字键
你确定吗?
如果我 运行 你的代码没有插件挂载,我会失败,因为它比较对象引用,
getStyles().then(oldStyles => {
// no plugin mounted
getStyles().then(newStyles => {
newStyles.forEach((newStyle, i) =>
expect(newStyle).to.equal(oldStyles[i])
);
});
但是如果我使用 .to.deep.equal
它会成功
getStyles().then(oldStyles => {
// no plugin mounted
getStyles().then(newStyles => {
newStyles.forEach((newStyle, i) =>
expect(newStyle).to.deep.equal(oldStyles[i])
);
});
getComputedStyle() returns 一个活动对象
The returned style is a live CSSStyleDeclaration object, which updates automatically when the element's styles are changed.
所以你需要在比较之前克隆结果,即使插件在你比较时改变了一些东西,它们也会是相同的。
我建议将 JSON/stringify()
应用于结果并比较字符串,它非常快,也不需要深度相等。
function getStyles() {
return cy.get('.el-on-the-page *').then((elements) => {
const styles = []
elements.each((_, el) => {
styles.push(window.getComputedStyle(el));
});
return JSON.stringify(styles);
});
}
getStyles().then(oldStyles => {
mountPlugin();
getStyles().then(newStyles => {
expect(newStyles).to.equal(oldStyles);
});
});
设置
我正在为网站编写插件。它将通过插件的 CSS 向 DOM 添加元素。我希望样式仅限于插件,即一旦插件包含在网页中,插件之外的任何元素都不应更改其外观。
我正在 运行使用 cypress 进行集成测试。当插件包含在页面上时,我如何断言所有预先存在的元素的样式保持不变?我可以访问插件加载前后的页面。
方法
这是我认为应该有效的方法:
cy.visit('theURL');
getStyles().then(oldStyles => { // Get the styles of the elements
mountPlugin(); // Mount the plugin (including CSS)
getStyles().then(newStyles => { // Get the (possibly changed) styles
newStyles.forEach((newStyle, i) => // Compare each element’s style after
expect(newStyle).to.equal(oldStyles[i]) //+ mounting to the state before mounting
);
});
});
function getStyles() {
return cy.get('.el-on-the-page *').then((elements) => { // Get all elements below a certain root
const styles: CSSStyleDeclaration[] = []
elements.each((_, el) => { // Get each element’s style
styles.push(window.getComputedStyle(el)); //+ and put them it an array
});
return styles; // Return the styles
});
}
问题
CSSStyleDeclaration
中的数字键行 expect(newStyle).to.equal(oldStyles[i])
失败,因为 oldStyles[i]
包含仅列出 属性 名称的数字键。例如,
// oldStyles[i] for some i
{
cssText: "animation-delay: 0s; animation-direction: normal; […more]"
length: 281
parentRule: null
cssFloat: "none"
0: "animation-delay" // <-- These elements only list property names, not values
... //+
280: "line-break" //+
alignContent: "normal" // <-- Only here there are actual property values
... //+
zoom: "1" //+
...
}
解决方法
我通过手动循环 CSS 键并检查键是否为数字来解决此问题。但是,这些数字键只出现在oldStyles
中,而没有出现在newStyles
中。我写这篇文章是因为这对我来说很可疑,我认为错误可能已经存在。
// Instead of newStyles.foreach(…) in the first snippet
newStyles.forEach((newStyle, i) => {
for (const key in newStyle) {
if(isNaN(Number(key))) {
expect(newStyle[key]).to.equal(oldStyles[i][key]);
}
}
});
空 属性 值
我在这里隐含假设 DOM 实际上已加载并应用了样式。根据我的理解,getLinkListStyles
对 cy.get
的调用应该仅在 cy.visit
等待 window 触发 load
之后安排到 运行事件。
cy.visit()
resolves when the remote page fires itsload
event.
但是,采用上述解决方法后,我在 oldStyles
中得到了 CSS 规则的空字符串。例如:
//oldStyles[i] for some i
{
cssText: "animation-delay: ; animation-direction: ; animation-duration: ; […more]"
length: 0
parentRule: null
cssFloat: ""
alignContent: ""
...
}
尝试过的解决方案
请注意,当我明确使用带有 cy.visit
的回调时,此行为不会改变,即:
cy.visit(Cypress.env('theURL')).then(()=>{
getStyles().then((oldStyles) => {
// (rest as above)
在getStyles()
开头的cy.wait(15000)
也没有:
function getStyles() {
cy.wait(15000); // The page has definitely loaded and applied all styles by now
cy.get('.el-on-the-page *').then((elements) => {
...
我无法回答有关空 属性 值的问题,解决方法应该不会影响事情。如果我没理解错的话,你在不使用解决方法时会得到 属性 值?
数字键
这些几乎肯定是 cssText 样式的索引,即 内联样式 。
数字键的数量与 cssText
中的条目完全相同,并且值与 cssText
中键值对的 LHS 匹配。
第 2 个 getStyles() 缺少数字键
你确定吗?
如果我 运行 你的代码没有插件挂载,我会失败,因为它比较对象引用,
getStyles().then(oldStyles => {
// no plugin mounted
getStyles().then(newStyles => {
newStyles.forEach((newStyle, i) =>
expect(newStyle).to.equal(oldStyles[i])
);
});
但是如果我使用 .to.deep.equal
它会成功
getStyles().then(oldStyles => {
// no plugin mounted
getStyles().then(newStyles => {
newStyles.forEach((newStyle, i) =>
expect(newStyle).to.deep.equal(oldStyles[i])
);
});
getComputedStyle() returns 一个活动对象
The returned style is a live CSSStyleDeclaration object, which updates automatically when the element's styles are changed.
所以你需要在比较之前克隆结果,即使插件在你比较时改变了一些东西,它们也会是相同的。
我建议将 JSON/stringify()
应用于结果并比较字符串,它非常快,也不需要深度相等。
function getStyles() {
return cy.get('.el-on-the-page *').then((elements) => {
const styles = []
elements.each((_, el) => {
styles.push(window.getComputedStyle(el));
});
return JSON.stringify(styles);
});
}
getStyles().then(oldStyles => {
mountPlugin();
getStyles().then(newStyles => {
expect(newStyles).to.equal(oldStyles);
});
});